ArcGIS&API&for&Silverlight开发入门(2):一个基础地图实例
这节在一个地图实例的基础上,来对Silverlight
API中的一些基本概念做一个总体了解,顺便熟悉一下Silverlight的开发知识。
&&&,直接看效果。

TITLE="ArcGIS&API&for&Silverlight开发入门(2):一个基础地图实例" />

TITLE="ArcGIS&API&for&Silverlight开发入门(2):一个基础地图实例" />
(722.07 KB)
&&&根据上一节的知识,可以知道这个Silverlight程序里包含了一个Map控件,并且里面至少有一个WorldImagery的图层。那么Page.xaml里的关键代码开起来应该是这样的:
Url="/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer"
所有的布局工作都在一个Grid中进行,给它起个名字叫LayoutRoot。Grid里面放了一个esri:Map元素(Map控件),它继承自Silverlight的Control,所以拥有Width和Height属性,默认是Auto,自动填充整个Grid。Map.Layers是一个集合,可以往里面添加layer,这里的layer指的是ArcGIS
Server或其他软件发布的地图服务,目前SilverlightAPI中支持的能够直接使用的有ArcGISDynamicMapServiceLayer,ArcGISTiledMapServiceLayer,ArcGISImageServiceLayer,分别对应ArcGIS
Server发布的动态地图服务,缓存地图服务(两种Map
Service)和ImageService,这三种图层是拿来即用的,如果你想加入别的地图服务,比如WMS服务,则需要自己继承相应类型的的Layer;此外还有GraphicsLayer,ElementLayer,SilverlightAPI特有的FeatureLayer等。这些都会在之后的小节中讲到。强调一下,与ADF开发里MapResourceManager一样,在Map中加入的内容实际上是地图服务,但当做一个layer处理。
&&&下面就对这个例子中的每一部分来做说明(与上图中的序号相对应)。
1、当地图移动时获取地图范围。
&&&当地图范围改变后,显示出当前地图范围的边界值。
&&&这部分的页面布局是这样的:
有关xaml中详细的布局知识请大家参照其他例子学习,这里稍作讲解。外面的Gridright这个Grid就是页面右边1、2、3、6的父容器,之所以不用StackPanel是因为6需要贴着页面底部,StackPanel中的元素都会flow贴到一起。三个矩形组合便构成了整体轮廓,由于它们都在一个Canvas中,所以会产生压盖效果。最先加入的rectBottom这个矩形便是最底下的阴影效果,中间的矩形是蓝色框,最上面的矩形是白色的文字显示区域。“{
}”里的内容在xaml中称作markupextention,StaticResource是使用在别处已经定义好的资源(resource)来对本元素的一些属性进行自动赋值,这里用来修饰Rectangle的外观。xaml中除了StaticResource这种markupextention之外还有Binding和TemplateBinding两种markup
extention,分别用于数据绑定(databinding)和自定义control的外观。上面的StaticResource是在App.xaml中定义的,这样就可以在本工程的任何页面中使用,当然也可以定义为LayoutRoot这个Grid的Resource。贴出来大家一看就明白了:
它们就相当于网页中的css。如果不使用StaticResource,那么三个矩形看起来应该是这样的:
你猜的没错,在其他矩形框部分也使用到了这些属性。通过实践可以感受到,xaml中的布局在一般使用中比html+css的布局要简单和灵活许多。好了,继续。
&&&Map控件里面已经封装了一些事件来供我们使用,我们可以在需要的时候捕获它们来进行处理。如果做过ArcGIS产品的二次开发,你应该已经想到我们要捕获的就是Map的ExtentChanged事件;而要在地图移动或者缩放的过程中也实时显示地图范围,则还要对ExtentChanging事件做处理。细心的你可能已经发现,在上面的xaml代码中已经对世界地图这个图层的Initialized事件添加了一个hanlder:WorldImageLayer_Initialized。当然可以像这样一样给Map的这两个事件添加handler,但这里并不这么做,而是在世界地图图层的Initialized事件里来绑定它们(移动地图时出发ExtentChanged事件,网速过慢导致图层并未加入到Map中,则会报错)。来看看Page.xaml.cs中的code-behind代码:
private void WorldImageLayer_Initialized(object sender,
EventArgs e)
Map1.ExtentChanged += new
EventHandler(Map1_ExtentChange);
Map1.ExtentChanging += new
EventHandler(Map1_ExtentChange);
没错,把两个事件绑定到同一个handler即可。再看看Map1_ExtentChange中的代码:
private void Map1_ExtentChange(object sender,
ESRI.ArcGIS.ExtentEventArgs e)
TBextent.Text =
string.Format("地图范围:\nMinX:{0}\nMinY:{1}\nMaxX:{2}\nMaxY:{3}",
e.NewExtent.XMin, e.NewExtent.YMin, e.NewExtent.XMax,
e.NewExtent.YMax);
很简单吧?顺便提一下,ExtentEventArgs里既然有NewExtent,当然就有OldExtent了,通过比较这两个变量就可以分析出当前进行的是放大、缩小还是平移操作了。其实还有个更简单的办法,查查看Map的Resolution属性吧。
对于Silverlight API中内容,是不是感觉很容易呢(当然你得做够xaml的功课才行)?那么赶快来看第二部分。
2、当鼠标移动时获取鼠标坐标。
&&&包括屏幕坐标和地图坐标。外观样式方面是这样的:
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="屏幕坐标:" TextWrapping="Wrap" FontWeight="Bold" /&
HorizontalAlignment="Left" VerticalAlignment="Center"
Text="地图坐标:" TextWrapping="Wrap" FontWeight="Bold" /&
那么接下来要捕捉那个事件呢?当然就是MouseMove啦。不过如果查看SilverlightAPI中的Map类,发现并没有这个事件。但要记住Map是继承自xaml中的Control,Control继承自FrameworkElement,FrameworkElement继承自UIElement,这里就有一个MouseMove事件了。所以Map控件的MouseMove是xaml中而不是Siverlight
API中的事件(当然整个SilverlightAPI都是建立在xaml基础上的)。在esri:Map标签中添加一个MouseMove事件(MouseMove="Map1_MouseMove"),来看看code-behind代码:
private void Map1_MouseMove(object sender, MouseEventArgs
if (Map1.Extent != null)
System.Windows.Point screenPnt =
e.GetPosition(Map1);
TBscreencoords.Text = string.Format("屏幕坐标:\nX:{0},Y:{1}",
screenPnt.X, screenPnt.Y);
ESRI.ArcGIS.Geometry.MapPoint mapPnt =
Map1.ScreenToMap(screenPnt);
TBmapcoords.Text = string.Format("地图坐标:\nX:{0}\nY:{1}",
Math.Round(mapPnt.X, 4), Math.Round(mapPnt.Y, 4));
可以看到Map控件提供了屏幕与地图坐标之间转换的方法,好比开发人员的一座桥梁,用来往返于Silverlight特性与地图之间,非常方便。需要说明的是,这里GetPosition(Map1)获得的屏幕坐标是相对于Map控件的,而不是显示器的左上角。ok,继续来看第三部分。
3、Map里的动画效果。
&&&当地图放大和平移时都可以看到平滑的效果,这归功于Silverlight的动画功能。Map在封装完动画效果后,给了我们两个属性来对它们进行设置:PanDuration和ZoomDuration,用于设置这两个动作持续的时间。它们都是TimeSpan类型的变量,合理的设置可以带来良好的用户体验。看看这部分的布局:
LargeChange="5" Cursor="Hand"
ValueChanged="slideranimation_ValueChanged" Width="180"
LargeChange="5" Cursor="Hand"
ValueChanged="slideranimation_ValueChanged" Width="180"
主要用到了两个slider控件。再看看拖动滑块时的事件代码,为了省事,这两个事件也用了同一个handler:
private void slideranimation_ValueChanged(object sender,
RoutedPropertyChangedEventArgs e)
Slider s=sender as S
if (s.Name == "sliderzoomanimation")
Map1.ZoomDuration = new TimeSpan(0, 0,
Convert.ToInt32(sliderzoomanimation.Value));
TBzoomdurationvalue.Text = string.Format("当前值:{0}秒",
Convert.ToInt32(sliderzoomanimation.Value));
Map1.PanDuration = new TimeSpan(0, 0,
Convert.ToInt32(sliderpananimation.Value));
TBpandurationvalue.Text = string.Format("当前值:{0}秒",
Convert.ToInt32(sliderpananimation.Value));
对应着地图效果,应该很容易理解。继续第四部分。
4、对地图服务可见性与动态地图服务中图层可见性的控制。
&&&还是要强调一下,WorldImagery和StreetMap两个能看到的地图实际上都是地图服务,当作layer加入到了Map控件中;而动态地图服务USA中的图层Cities,Rivers,States才是与ArcMap中图层相对的概念。对于WorldImagery和StreetMap之间的切换,主要用到了Silverlight
API里Layer的
Visible属性;而动态服务中图层可见性的操作,主要是对ArcGISDynamicMapServiceLayer的VisibleLayers数组做了设置。
&&&StreetMap这个服务其实一开始就加入了地图(在esri:Map标签中):
Url="/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer"
Visible="False" /&
而设置了Visible="False"。图层不可见时地图不会对它做任何处理,所以不用担心会耗费流量或加重程序负担。
&&&看看布局部分:
这里使用了ToggleButton,CheckBox和RadioButton都由它派生而来。Silverlight2中的ToggleButton不能设置Group(一个Group中自动限定同时只能有一个控件处于激活状态),不如Flex里的ToggleButton来的方便,所以code-behind中多做了些工作。当然这里使用RadioButton也是可以的。
private void TBimagery_Clicked(object sender, RoutedEventArgs
if (TBstreetmap.IsChecked==true)
Map1.Layers["WorldImageLayer"].Visible =
Map1.Layers["WorldImageLayer"].Opacity = 0;
TBstreetmap.IsChecked =
Storyboard sbworldmapshow = makestoryboard("WorldImageLayer",
Storyboard sbstreetmaphide = makestoryboard("StreetMapLayer",
sbworldmapshow.Begin();
sbstreetmaphide.Begin();
hidelayername = "StreetMapLayer";
timer.Begin();
TBimagery.IsChecked =
private void TBstreetmap_Clicked(object sender, RoutedEventArgs
if (TBimagery.IsChecked==true)
Map1.Layers["StreetMapLayer"].Visible =
Map1.Layers["StreetMapLayer"].Opacity = 0;
TBimagery.IsChecked =
Storyboard sbstreetmapshow = makestoryboard("StreetMapLayer",
Storyboard sbworldmaphide = makestoryboard("WorldImageLayer",
sbstreetmapshow.Begin();
sbworldmaphide.Begin();
hidelayername = "WorldImageLayer";
timer.Begin();
TBstreetmap.IsChecked =
private void timer_Tick(object sender, EventArgs e)
Map1.Layers[hidelayername].Visible =
public Storyboard makestoryboard(string layername, double from,
double to)
Storyboard sb = new Storyboard();
ESRI.ArcGIS.ArcGISTiledMapServiceLayer layer =
Map1.Layers[layername] as
ESRI.ArcGIS.ArcGISTiledMapServiceLay
DoubleAnimation doubleAnim = new DoubleAnimation();
doubleAnim.Duration = new TimeSpan(0, 0, 5);
doubleAnim.From =
doubleAnim.To =
Storyboard.SetTarget(doubleAnim, layer);
Storyboard.SetTargetProperty(doubleAnim, new
PropertyPath("Opacity"));
sb.Children.Add(doubleAnim);
当切换两个地图服务时能够看到一个渐变的效果,这里用到了Silverlight中的动画,它们都是在StoryBoard里面进行的,以后的小节中会讲Silverlight中的动画,这里不再废话了,有兴趣的朋友可以自己参考帮助学习。hidelayername是这个一个公用的string变量,用来在切换的动画效果完成后设置不可见的图层Visible属性。timer也是一个StoryBoard:
这里可以看出把StoryBoard也能巧妙的用作计时器。到了特定时间(5秒)后会自动timer_Tick函数,当然也可以使用.net中的各种timer类。
&&&下面是添加动态服务的部分。
private void chkboxDynamicLayer_Click(object sender,
RoutedEventArgs e)
if (chkboxDynamicLayer.IsChecked == true)
Map1.Layers.Add(california);
Map1.ZoomTo(california.FullExtent);
if (california.IsInitialized == false)
chkboxDynamicLayer.IsEnabled =
chkboxDynamicLayer.Content = "去掉它";
SVlayers.Visibility = Visibility.V
Map1.Layers.Remove(california);
chkboxDynamicLayer.Content = "添加一个动态图层吧";
SVlayers.Visibility = Visibility.C
private void dynamiclayer_initialized(object s, EventArgs
//若图层没有初始化好就移除图层,当然会报错了,所以这样做就不会了
chkboxDynamicLayer.IsEnabled =
Map1.ZoomTo(california.InitialExtent);
SVlayers.Visibility = Visibility.V
california.ID = "layercalifornia";
ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer =
s as ESRI.ArcGIS.ArcGISDynamicMapServiceL
if (dynamicServiceLayer.VisibleLayers == null)
dynamicServiceLayer.VisibleLayers =
GetDefaultVisibleLayers(dynamicServiceLayer);
UpdateLayerList(dynamicServiceLayer);
当添加了动态服务后,会自动弹出一个listbox,当然这些也都是在xaml中定义好的(加在上面的Canvas后面):
Tag="{Binding ServiceName}" IsChecked="{Binding
ClickMode="Press" Click="chkboxToggleVilible_Click"
这里把ListBox放到了ScrollVierwer中,固定了它的高度,当内容过多时可以自动显示纵向滚动条。这里要提一下,ListBox的内容用到了数据绑定(参考xaml中的DataBinding,有OneTime,OneWay和TwoWay三种模式,这里使用的是默认的OneWay),看起来里面只有一个CheckBox,但它相当于一个模板,在code-behind中设置了ListBox.ItemSource之后,根据该属性的内容自动生成多个CheckBox。代码中自定义了一个LayerListData类,它的几个属性分别与上面的CheckBox属性绑定;将一个List赋给了ListBox.ItemSource,则会自动生成ListBox中的内容。通过一个List类型变量,来控制动态服务的可见图层。代码如下:
public class LayerListData
public bool Visible { }
public string ServiceName { }
public string LayerName { }
public int LayerIndex { }
private int[]
GetDefaultVisibleLayers(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer
dynamicService)
List visibleLayerIDList = new List();
ESRI.ArcGIS.LayerInfo[] layerInfoArray =
dynamicService.L
for (int index = 0; index & layerInfoArray.L
if (layerInfoArray[index].DefaultVisibility)
visibleLayerIDList.Add(index);
return visibleLayerIDList.ToArray();
private void
UpdateLayerList(ESRI.ArcGIS.ArcGISDynamicMapServiceLayer
dynamicServiceLayer)
int[] visibleLayerIDs =
dynamicServiceLayer.VisibleL
if (visibleLayerIDs == null)
visibleLayerIDs =
GetDefaultVisibleLayers(dynamicServiceLayer);
List visibleLayerList = new List();
ESRI.ArcGIS.LayerInfo[] layerInfoArray =
dynamicServiceLayer.L
for (int index = 0; index & layerInfoArray.L
visibleLayerList.Add(new LayerListData()
Visible = visibleLayerIDs.Contains(index),
ServiceName = dynamicServiceLayer.ID,
LayerName = layerInfoArray[index].Name,
LayerIndex = index
LayerVisibilityListBox.ItemsSource =
visibleLayerL
void chkboxToggleVilible_Click(object sender, RoutedEventArgs
CheckBox tickedCheckBox = sender as CheckB
string serviceName = tickedCheckBox.Tag.ToString();
bool visible = (bool)tickedCheckBox.IsC
int layerIndex = Int32.Parse(tickedCheckBox.Name);
ESRI.ArcGIS.ArcGISDynamicMapServiceLayer dynamicServiceLayer =
Map1.Layers[serviceName] as
ESRI.ArcGIS.ArcGISDynamicMapServiceL
List visibleLayerList =
dynamicServiceLayer.VisibleLayers != null
? dynamicServiceLayer.VisibleLayers.ToList() : new
if (visible)
if (!visibleLayerList.Contains(layerIndex))
visibleLayerList.Add(layerIndex);
if (visibleLayerList.Contains(layerIndex))
visibleLayerList.Remove(layerIndex);
dynamicServiceLayer.VisibleLayers =
visibleLayerList.ToArray();
5、比例尺。
&&&Silverlight
API提供了一个ScaleBar类,可以方便的设置地图比例尺。
需要在初始化的时候设置scalebar的Map属性,顺便来看看整个页面的初始化工作:
namespace demo_02_extendedmap
public partial class Page : UserControl
private ESRI.ArcGIS.ArcGISDynamicMapServiceLayer california =
new ESRI.ArcGIS.ArcGISDynamicMapServiceLayer();
public Page()
InitializeComponent();
scalebar.Map = Map1;
scalebarstoryboard.Begin();
TBzoomdurationvalue.Text = string.Format("当前值:{0}.{1}秒",
Map1.ZoomDuration.Seconds,
Map1.ZoomDuration.Milliseconds);
TBpandurationvalue.Text = string.Format("当前值:{0}.{1}秒",
Map1.PanDuration.Seconds,
Map1.PanDuration.Milliseconds);
california.Url =
"/ArcGIS/rest/services/Specialty/ESRI_StatesCitiesRivers_USA/MapServer";
california.Opacity = 0.5;
california.Initialized += new
EventHandler(dynamiclayer_initialized);
TBimagery.IsChecked =
makestoryboard("WorldImageLayer", 0, 1).Begin();
//切换全屏/窗口
Application.Current.Host.Content.FullScreenChanged += new
EventHandler(fullscreen_changed);
scalebarstoryboard是xaml里自定义的一个动画,效果见比例尺旁的单位。
6、地图相关操作。
&&&Map控件已经内置了一些键盘鼠标事件,但目前不能像JavascriptAPI中那样禁用这些事件。这里还用到了Silverlight程序的一个全屏特性,其实是对Application.Current.Host.Content的一个属性做了设置。直接看代码吧:
Text="地图操作提示:双击放大 Shift+拖拽:放大到指定范围 Ctrl+Shift+拖拽:缩小到指定范围"
放到Gridright Grid中,
private void TBfullscreen_Click(object sender, RoutedEventArgs
System.Windows.Interop.Content content =
Application.Current.Host.C
content.IsFullScreen=!content.IsFullS
private void fullscreen_changed(object o,EventArgs
System.Windows.Interop.Content
content=Application.Current.Host.C
TBfullscreen.IsChecked = content.IsFullS
7、进度条。
&&&最后还剩下地图中的这个进度条。利用了Map控件内置的一个Progress事件。
在esri:Map标签中加入一个事件:Progress="Map1_Progress",
private void Map1_Progress(object sender,
ESRI.ArcGIS.ProgressEventArgs e)
if (e.Progress & 100)
progressGrid.Visibility = Visibility.V
MyProgressBar.Value = e.P
ProgressValueTextBlock.Text = String.Format("正在处理 {0}%",
e.Progress);
progressGrid.Visibility = Visibility.C
好了到此就已经讲完了整个地图功能。尽管想尽可能详细说明每段代码,便于初学的朋友学习,但也不可能面面俱到。没有讲明白的地方大家可以自己思考,查帮助。学习的过程中,不思考,无进步。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。ArcGIS API for iOS开发教程(八)地图定位
源代码下载地址:
Apple系列的移动设备定位位置有两种方式,一是使用ArcGIS API for iOS中MapView中的GPS对象来获取当前位置;二是通过iPhone SDK中的CLLocationManager来获取。前者在获取当前位置的时候会受到设备的限制,如iPod Touch、iPad wifi版都是没有GPS模块的,因此,在使用mapView中的GPS对象时会失效,无法进行定位;后者在获取定位信息的时候,移动设备会首先使用 GPS,如果GPS无效则使用wifi或者蜂窝网络进行定位,这样,即便iPod Touch、iPad wifi版在联网情况下同样可以进行定位。
我们首先认识一下mapView的GPS对象。
1. – (void)viewDidLoad {
2. [super viewDidLoad];
3. //启动GPS
[self.mapView.gps start];
//检测GPS设备是否启动;
if (!self.mapView.gps.enabled) {
NSLog(@”The GPS is not enabled”);
NSLog(@”The GPS is enabled”);
鉴于前文分析的使用mapView中GPS对象的局限性,下面来介绍CLLocationManager的地图定位。
1、 按照【ArcGIS API for iOS开发之旅】mapView中介绍的步骤创建好一个基本的地图应用程序,命名为GeoLocatorDemo
将CoreLocation.framework加入到工程引用中
打开GeoLocatorDemoViewController.***件,定义CLLocationManager对象,并定义为属性;添加CLLocationManagerDelegate代理,代码如下:
1. @interface GeoLocatorDemoViewController : UIViewController&CLLocationManagerDelegate,AGSMapViewDelegate& {
AGSMapView
AGSGraphicsLayer
*graphicsL
AGSTiledMapServiceLayer *tiledL
CLLocationManager *locationM
7. @property (nonatomic, retain) IBOutlet AGSMapView *mapV
8. @property (nonatomic, retain) AGSGraphicsLayer
*graphicsL
9. @property (nonatomic, retain) AGSTiledMapServiceLayer *tiledL
10. @property (nonatomic, retain) CLLocationManager *locationM
打开GeoLocatorDemoViewController.m文件,在viewDidLoad函数中初始化locationManager,代码如下:
1. – (void)viewDidLoad {
[super viewDidLoad];
self.mapView.mapViewDelegate =
self.tiledLayer = [[AGSTiledMapServiceLayer alloc]
initWithURL:[NSURL URLWithString:kTiledMapServiceURL ]];
[self.mapView addMapLayer:tiledLayer withName:@”Tiled Layer”];
self.graphicsLayer = [AGSGraphicsLayer graphicsLayer];
[self.mapView addMapLayer:self.graphicsLayer withName:@”graphicsLayer”];
self.locationManager = [[[CLLocationManager alloc] init] autorelease];
self.locationManager.delegate = // send loc updates to myself
self.locationManager.distanceFilter = 1000;
// 1 kilometer
self.locationManager.desiredAccuracy = kCLLocationAccuracyK
实现AGSMapViewDelegate中的mapViewDidLoad函数,在该函数中启动位置的监控,代码如下:
[self.locationManager startUpdatingLocation];
实现CLLocationManagerDelegate中的-(void)locationManager: (CLLocationManager *)manager didUpdateToLocation: (CLLocation *)newLocation fromLocation: (CLLocation *)oldLocation方法,用于获取地理位置信息。在该方法中首先获取位置信息,之后将获取的经纬度信息转换成Web 墨卡托坐标信息,用来在地图上定位;最后漫游到指定级别
– (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
NSLog(@”Location: %@”, [newLocation description]);
[self.locationManager stopUpdatingLocation];
//获取位置信息
pnt.x = newLocation.coordinate.
pnt.y = newLocation.coordinate.
//坐标转换
CGPoint mecPoint = [self lonLat2Mercator:pnt];
AGSPoint *mappoint =[[AGSPoint alloc] initWithX:mecPoint.x y:mecPoint.y spatialReference:nil ];
[self.graphicsLayer removeAllGraphics];
AGSPictureMarkerSymbol *
pt = [AGSPictureMarkerSymbol pictureMarkerSymbolWithImageNamed:@”pushpin.png”];
// this offset is to line the symbol up with the map was actually clicked
pt.xoffset = 8;
pt.yoffset = -18;
AGSGraphic *pushpin = [[AGSGraphic alloc] initWithGeometry:mappoint symbol:pt attributes:nil infoTemplate:nil];
// add pushpin to graphics layer
[self.graphicsLayer addGraphic:pushpin];
[pushpin release];
[self.graphicsLayer dataChanged];
//漫游到指定级别
[self.mapView centerAtPoint:mappoint animated:YES];
int levelToZoomTo = 12;
AGSLOD* lod = [self.tiledLayer.mapServiceInfo.tileInfo.lods objectAtIndex:levelToZoomTo];
float zoomFactor = lod.resolution/self.mapView.
AGSMutableEnvelope *newEnv =
[AGSMutableEnvelope envelopeWithXmin:self.mapView.envelope.xmin
ymin:self.mapView.envelope.ymin
xmax:self.mapView.envelope.xmax
ymax:self.mapView.envelope.ymax
spatialReference:self.mapView.spatialReference];
[newEnv expandByFactor:zoomFactor];
[self.mapView zoomToEnvelope:newEnv animated:YES];
7、编译,运行,结果如图所示:
This entry was posted in , , , , , .
CAPTCHA Code *
Recent Posts