本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6421503.html
有人问我怎么这个系列没有写自己做的东西呢?
大哥大姐,这是“学习笔记”啊!当然主要以解读和笔记为主咯。
也有人找我要实例代码(不是示例),我表示AJS尚未成熟,现在数据编辑功能才简略地在AJS 4.3中出现,4.2是没有的,widget和分析功能也不是很完善,还是再等等吧,先学着基础,其他的以后再说。
本节我会紧随这个例子学习一下Query这个类,作为图层查询方法的重要参数,它起了传递查询用的信息的作用。
本例对应官方的例子是:Query a SceneLayer’s linked FeatureLayer
这个例子操作起来很简单,等地图加载完成后,点击对应的3D白色模型,会出现一个popupTemplate弹窗,显示一个表格,为这个大楼的信息。如下图:
换个视角:
那我们就开始吧!
1 | require([ |
数据方面,用到了场景图层和要素图层;
查询方面,用到了Query模块。
1 | function(Map, SceneView, SceneLayer, FeatureLayer, Query){ |
套路,先把大骨架弄出来,然后逐个击破。
多了一个场景图层,多了一个要素图层,在要素图层加载完成后进行一个异步操作attributesReady()。
还算简单,先把map和view的代码看看(太基础了对于看到这里的人来说,我就缩起来了)
1 | var map = new Map({ |
对于view,可以看到除了camera属性外还多了两个属性,这里不作为重点,可以参考API。
看看两个layer:
1 | var sceneLayer = new SceneLayer({ |
在我的理解中,sceneLayer应该是绿色的圆锥体,而featureLayer则是用于支持空间查询的数据图层。看得出这里都是使用了ArcGIS Server上的服务。
featureLayer的弹窗模板较为复杂,不是这里讨论的内容,我就把源代码换成了省略号以精简页面,大概意思就是从featureLayer中获取fields作为弹窗的content,而fieldInfos则是对应字段的输出格式。
重点应该是下面这个方法体:
1 | featureLayer.load().then(attributesReady); |
逐个击破,首先是的view的click事件,触发该事件后立刻获取当前点击的点screenPoint,并将点传递到hitTest方法中,紧接着就是四层异步操作。
第一层:
1 | .then(function(response) { |
似乎是获取results[0]后判断其是否为空或者其几何属性是否为空,如果不是空,返回其几何属性。
这里就要查一下了,这个response 是什么东西?而results[0]又是什么?
通过查阅SceneView的hitTest()方法,可以得知results[0]也就是result变量就是hitTest的返回值。
该result变量拥有两个属性,一个是mapPoint,为Point类型;
另一个是graphic,为Graphic类型。
前一个属性代表了点击的那个点位置,后面则为点击的要素的几何体。
如果都不为空,返回几何体,传入下一个then:
1 | .then(function(graphic) { |
传入的graphic,获取其objectid属性
然后实例化一个Query对象名为query,设置好where查询语句(应该是SQL语句吧)、是否返回几何体(否)、输出字段信息,就可以执行查询。
featureLayer的queryFeatures()方法接受Query对象,返回一个类型为FeatureSet的Promise对象。
这个FeatureSet对象传递给下一个then:
1 | .then(function(results) { |
不要放弃啊!
因为马上后面就没有代码了~那个otherwise只是Promise对象除了then之外的另一个而已~出错了就用otherwise来处理异常~
这个then,如果传入的FeatureSet不为空或者FeatureSet里面的features个数>0
那么就按照featureLayer里写好的格式弹窗显示信息~完事。
总结一下
这个例子就是获取点击点——使用query对象进行空间查询——返回FeatureSet——输出其预置好的Popup即可~
没有上一个那么难了吧~~~
文章的最后,我来对Query这个对象进行学习:
查询API得知:
继承自Accessor,分类在esri/tasks/support/Query下。
需要注意的是,使用Query类需要在ArcGIS Online或者ArcGIS Server服务的支持下进行。
翻译官方的解释,意思就是:
Query对象是一些空间查询的参数,它作用于layer对象,通过参数的设置,可以筛选出layer中需要的要素。
可以使用QueryTask类的excute()方法进行空间查询,也可以在图层实例的queryFeatures()方法中使用Query对象。
使用它,通常返回结果是FeatureSet对象。
Query类拥有非常多的参数,方法只有一个toJSON(),这个方法是将这个类的实例序列化成JSON而存在的,不做重点介绍。
参数就摘几个吧:
distance(Number类型)、geometry(Geometry类型)、num(Geometry类型)、outFields(String[])、outSpatialReference(SpatialReference)、returnGeometry(Boolean)、units(String)、where(String)
distance是缓冲区分析的距离;
geometry是用于空间查询用的几何体;
num是查询得到的要素个数;
outFields是查询结果要素集的字段信息;
outSpatialReference是查询结果要素集的空间参考信息;
returnGeometry是一个布尔变量,如果为真,那么返回的要素集中每一个要素就会包含几何体;
units是缓冲区分析的单位;
where是空间查询用的SQL语句。如:query.where = “NAME = ‘“ + stateName + “‘“;
如何用它呢?
给个简单的例子,使用QueryTask的(接下来几篇博文会用到这个):
1 | require(["esri/tasks/QueryTask", "esri/tasks/support/Query"], function(QueryTask, Query){ |
如果是图层的queryFeatures()方法,各位看官请往上翻一翻啦~
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6421488.html
这个例子相当复杂。我先简单说说这个例子是干啥的。
在UI上,提供了一个下拉框、两个滑动杆,以确定三个参数,使用这三个参数进行空间查询。这个例子就颇带空间查询的意思了。
第一个参数是油井类型,第二个参数是油井的缓冲半径,第三个参数是地震级别。
给定油井的类型,给定油井的缓冲半径(缓冲区分析生成),给定地震级别,就能在油井附近以这个缓冲半径为圆搜索出符合给定地震级别的地震点。
这个例子是干嘛的呢?
“因为开采油田会导致地下空间坍塌,而引发地震。”
看看搜索结果(随便选的参数):
橙色点即为搜索结果(地震点)。
给出引用
话不多说,看看这个例子的引用有多少:
1 | require( |
用到了两种Layer,FeatureLayer是数据图层,GraphicsLayer是缓冲区图层和结果显示图层。
为了支持GraphicsLayer的缓冲区,需要用到geometryEngine模块和Graphics模块。
为了支持结果显示,用到了SimpleFillSymbol模块和SimpleMarkerSymbol模块。
函数参数骨架
1 | function(Map, MapView, FeatureLayer, GraphicsLayer, Graphics, SimpleFillSymbol, SimpleMarkerSymbol) |
可吓坏我了,这么庞大的骨架(script标签就有200+行)。
经过我一个月的细读,我终于把这些流程大概弄懂了,如下图:
有些地方表述可能不太对。
分为两块,一块是事件体,另一块是函数体。
先讲简单的吧,事件体。
wellTypeSelect和distanceSlider这两个dom元素的change事件体都会独立触发createBuffer()函数体,wellTypeSelect事件则会先触发setWellsDefinitionExpression()函数体进行遍历搜索几何体,然后把返回值(地震点的几何体集合wellsGeometries)交给createBuffer()函数体,生成缓冲区。
意思是只要油井类型(下拉列表的项目改变)和距离滑块改变,缓冲区就要进行重绘。
distanceSlider的input事件会改变DOM上的数值,为缓冲区半径的数值。
magSlider的input事件会改变DOM上的数值,为地震级别的数值。
queryQuakes按钮的click事件会通过已经生成的缓冲区进行叠置分析,搜索缓冲区内的地震点。
对大量的变量如果不是很懂,这里可以先缓缓,下面的函数体会更详细介绍,事件体主要明白DOM的事件能干啥就行了。
有几个函数体是连锁反应的,而且是在MapView的then中进行连锁异步操作的。
在这里我又不得不提一下异步操作和这个then的用法了。
then的含义就是,等then前面的操作在服务器上做完后,再执行then里面的内容。
因为js是单线程的解释型语言,不会编译,只会从头到尾一次读下来,所以执行到then前面的代码时,是不可能停在那里的。
then前面的代码执行完肯定有一个类似回执的东西(可以理解为返回值),但是浏览器不能一直等待这个回执啊?
所以就在then里面写一个回调函数,丢给服务器,在服务器端等待then前的操作完成后,再把“返回值”返还给回调函数做,即可。
then()里的东西不会立刻在本地执行,在本地等待前一步的操作结果只会让浏览器卡死。
实际上,我标红的关键词,就是异步操作的思想了。
复习完异步操作和then,我们再看看view的那一串then:
1 | view.then(function() { |
我直接给出我的解读:首先,view加载完后,执行第一个then,第一个then返回wellsLayer的空间搜索(queryFeatures()这个方法)结果,这个结果是FeatureSet类型的,名为response。这response在哪用呢?它传递给了getValues()这个方法体:
1 | function getValues(response) { |
经查,response确实就是FeatureSet类的实例,通过getValues获取其内所有features的所有STATUS2字段的值,名为values,类型为AJS中的Collection类型。
values又传递给getUniqueValues()方法体:
1 | function getUniqueValues(values) { |
根据某些规则(if语句)遍历values,使用Collection类的forEach方法,然后把遍历结果作为JS的数组返回,名为uniqueValues,传递给
addToSelect()方法体,实现选择列表这个DOM元素上出现油井类型(option):
1 | function addToSelect(values) { |
不能光添加呀!所以它就return了,方法setWellsDefinitionExpression()的结果。
setWellsDefinitionExpression()这个方法是设置wellsLayer的SQL查询语句的,但是没完,setWellsDefinitionExpression()这个方法内还有一层return:
1 | function setWellsDefinitionExpression(newValue) { |
queryForWellGeometries()这个方法是setWellsDefinitionExpression()这个方法必定会触发的方法体,它用于搜索wellsLayer中的几何体。
注意到了吗?空间查询是需要某个Layer的query对象的。
在wellsLayer中,使用createQuery()方法就能返回一个query对象,query对象包含了所有用于空间查询(搜索)用的信息,通过它,才能使用queryFeatures等方法对需要进行空间查询(搜索)的图层进行空间查询(搜索)。
所以图中的连串的then对应的方法体就已经解释完毕了。(呼呼~不知道各位能不能坚持到这,也不知道各位能不能看懂…)
还剩三个方法体,createBuffer()、queryEarthQuakes()和displayResults()方法体。
先来看看createBuffer():
1 | function createBuffer(wellPoints) { |
view.graphics.removeAll();
view.graphics.add(bufferGraphic);
}
从DOM元素中获取缓冲区半径,然后用geometryEngine这个工具集(或者叫类,还没到空间分析章节就不说那么多了)中的geodesicBuffer方法产生缓冲区,数据是传入的参数wellPoints,即上面addToSelect()方法中最后一层返回的几何体集合(油井点集合)。
然后实例化Graphic实例,设置颜色、线宽,然后添加到view中的graphics容器内即可。
再看queryEarthQuakes()和displayResults():
1 | function queryEarthquakes() { |
前一个设置query(又是query!)的信息,返回quakesLayer搜索的结果(看下一个方法,应该是FeatureSet类型的),结果传递给displayResults(在按钮的click事件中通过then异步传递),名为results。
在后一个方法体中,使用map方法遍历要素中所有几何体,设置线宽和色彩等符号样式,最后在DOM元素上刷新显示搜索得到了多少个地震点“numQuakes earthquakes found”,并在结果图层中添加这些要素,刷新显示。
最后两个其实就是通过上方生成的缓冲区进行空间查询(搜索),得到结果并刷新显示的过程。
至此,本例就全部解释完毕了,至于有些变量是什么,看new的类型就知道啦,一路看我的文章应该都懂的。
————
感言:一个月了,好拖啊…4.3都出了这么久了,才把这个例子攻克。嗯,接下来的例子就不会那么又臭又长了…
本例通过对图层的query对象的使用,以及结合空间分析中的缓冲区(使用geometryEngine),进一步加深了异步操作和遍历的代码理解。
要说能符合本章空间查询(搜索)的核心代码,也不过是wellsLayer.createQuery()这一句代码和query对象的属性设置了,Graphics的操作(遍历、属性设置)和结果的互相传递都不是本章的重点,所以导致了这个例子阅读困难。
大致流程为:从featureLayer中获取字段值,并加到下拉选择列表中——选择某个油井类型,设置缓冲半径,后台获取油井的点几何体集合,生成缓冲区——使用缓冲区,和设置好的地震级别,进行空间查询,得到地震点的搜索结果,刷新显示。
over!
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6421297.html
上一篇中提到,空间搜索小部件是Search这个类的实例化,作为视图的ui属性添加进去后,视图就会出现搜索框了。
这节的主体代码和上篇几乎一致,区别就在上篇提及的sources属性。
先看看结果:
由于不太清楚要素图层里有什么东西,随便输了个字母匹配,结果如图,中央出现了一个图案并弹窗。
开始讲课!
给出引用
1 | require( |
PictureMarkerSymbol是上图中搜索结果的图案所需的模块。
可见此例子采用了要素图层来进行搜索。
函数参数骨架
1 | function(Map, MapView, Search, FeatureLayer, PictureMarkerSymbol){ |
上一节提到Search这个类有一个重要的属性sources,它是Collection类型(同对象数组容器,与.NET中list容器差不多)。
看看完整代码:
1 | sources: [{ |
我这里没有缩起来,原因就是已经很明显了——
sources给了一个Object数组,数组内有两个{}对象。
每个{}对象拥有以下属性:【featureLayer,searchFields,suggestionTemplate,exactMatch,outFields,name,zoomScale,resultSymbol】
查阅API,得知sources接受以下数据类型作为搜索源:
featureLayerSource
locatorSource
在上面,sources[{…},{…}]中的每个大括号对象就是featureLayerSource类型的。这里有点拗口,featureLayerSource和locatorSource不是js中的类,而是一种“说法”,因为sources接受的是Object数组作为参数,只不过本例以featureLayerSource作为示范而已。
【featureLayerSource】可选参数
displayField(String):用于显示结果的字段(名)
exactMatch(Boolean):是否精确搜索,默认是否(false)。
featureLayer(FeatureLayer):这个参数必须需要,因为是数据源啊。
searchFields(String[]):用于搜索的字段(名)。
searchQueryParams(Object):包括outSpatialReference、returnGeometry、num、outFields、where、maxAllowableOffset、objectIds
suggestQueryParams(Object):包括outSpatialReference、returnGeometry、num、outFields、where
以上两个Object类型的参数不知道是干嘛用的,前一个似乎是搜索时的默认选项,后一个是请求建议时的默认选项(与Search类的suggest()方法有关)?
suggestionTemplate(String):displayField有多个时,需要有格式地显示,就用这个。例子:suggestionTemplate: “Name: {OWNER}, Parcel: {PARCEL_ID}”
再看看locatorSource:
【locatorSource】可选参数
categories(String[])
countryCode(String)
localSearchOptions(Object)
locationToAddressDistance(Number)
searchTemplate(String)
locator(Locator)
singleLineFieldName(String)
关于locatorSource就不说多了,这个数据源是对Locator(定位)类熟练运用才能使用的,因为前面的笔记没有对Locator有多余的描述,故仅仅在此记录。
回到sources[{…},{…}]的代码部分。
这样思路就清晰了,使用featureLayerSource作为搜索数据源,就要定义赋值上面提到的属性。
在featureLayer属性中,使用了popupTemplate方便输出。
在第二个featureLayerSource中,出现了一个新的东西——“resultSymbol”,它是PictureMarkerSymbol类的属性。查询API:
简单,这就是用一张图片指示出某个点。
这里用到了url、height、width三个属性,不必说多也知道是什么意思了。常用的属性还有xoffset、yoffset等。
总结一下。
如何在搜索小部件中使用多源数据呢?
只需要设置Search类的sources属性即可,可以有两种类型:featureLayerSource和locatorSource。
注意,虽然是这么说,但是写法上还是属于Object类型的。两个类型都需要设置必要的、可选的属性才能赋给sources属性。
最后给出完整的官方代码:
1 |
|
注意与html同级别下有一个image文件夹,里面存有senate.png图片文件。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6421297.html
这个例子很简单,作为开学后(暴露出学生党的本质)的开胃菜是再合适不过了。
不过,博主提前警告一下:接下来的例子会相当的长、烦、难。我还会用“引用”-“函数参数骨架”-“逐级分解代码”的模式,从上往下解读一些更难的AJS代码。
现在上课!
空间查询,是GIS的一个重头戏,除了基础定义(坐标系统、投影、符号等)、数据准备外,紧接着就是空间查询和下一章的空间分析。
对于AJS的小部件,我们已经接触了两个了,如2D地图中的指北针和上一章的弹窗(Popup)。这个例子也是小部件的应用,名为:“Search”。
本例是最普通的空间查询,即在3D地图中搜索地物。
举例:
在搜索小部件中输入上海,结果就会以goTo()动画跳转到搜索结果中。
给出引用
1 | require( |
可以看到,使用了widgets/Search引用。
函数参数骨架
1 | function(Map, SceneView, Search){ |
是不是很简单?
在常规的map和view实例化后,实例化一个Search对象,名为searchWidget,然后在view.ui属性中添加即可,空间搜索原来这么容易?
我们展开Search({…})和add()中的内容。
1 | var searchWidget = new Search({ |
要不要太简单?
Search()中是把view属性绑定到上方的view对象,add()中是确定搜索小部件的位置信息。
于是这个例子就完了。
查阅API:
Search是个类,继承自Accessor类。
它提供了为地图搜索地物的功能,对于要素图层,用空间查询更佳。
它显示为:
它的属性和方法很多,列举几个:
属性:view、popupTemplate、sources、viewModel、container,等等。
方法:on()、search()、clear()、destroy(),等等。
可以看出,Search类支持定义弹窗模板。
view.ui
ui是DefaultUI类的属性,在MapView的帮助文档中,查阅得到常用的方法是:
add()、remove()、move()、empty()。
而DefaultUI类继承自UI类,UI类继承自Accessor类。
add()方法接受两个参数,前一个参数是html元素(组),这里是searchWidget;后一个参数是小部件的位置,有两个可选参数:position和index。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399661.html
空间分析和空间查询是WebGIS有别于其他Web平台的特点。到这一章,就开始步入空间分析的内容了。
【Search widget】
介绍空间查询的核心小部件“Search”。
【Search widget with multiple sources】
在多种信息源中进行空间搜索。
【Query features from a FeatureLayer】
这个是在要素图层中进行查询。注意,查询(Query)和搜索(Search)的区别。
(其实我也不是很清楚这个的界限)
感觉查询就是在地理数据层面的遍历,结果是定制的。而搜索更强调单规则的查找。
这个例子用地震地点来做示范,半径可以指定,地震级别也可以指定。
【SceneLayer - query a linked FeatureLayer】
这个例子比较复杂。
在3D视图下进行点击后,把点击到的点映射到2D的FeatureLayer上,然后进行信息查询,然后把查询的结果用弹窗的形式显示出来。
【Qurey using QueryTask】
这个例子是用QueryTask这个类来进行空间查询。具体和上面的有什么不同,到代码中就知道了。
【Use FindTask without a map】
这个例子比较清新脱俗,没有地图的情况下,进行查询,结果也不在地图上显示,而在表格中显示。是基于什么原理呢?
【IdentifyTask】
识别任务“IdentifyTask”,一个类。识别结果在弹窗中显示。
最后三个例子有个共同点,就是都有“Task”,Task是一个类,继承自Accessor。学习后进行补充。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399657.html
这节对Popups这一章的最后两个例子进行介绍和解析。
第一个【Popup Actions】介绍了弹窗中如何自定义工具按钮(名为actions),以PopupTemplate+FeatureLayer的形式测量要素的长度为例子进行介绍。
第二个【Custom popup actions per feature】则是上一个的升级,如果说上一个例子的功能是写死的,那么这个例子就把这个功能写活了。什么意思呢?上个例子的测距仅仅能测距,没有什么别的特别的。而这个例子以啤酒店的分布(点要素图层)为例,在自定义的按钮中弹出在谷歌搜索的结果甚至弹出这个啤酒店的网站。
actions是什么?
actions是Popup类的一个属性,类型是Collection,即同类型AJS对象或原生JS对象的数组容器。
它能装什么?
能装一些“动作”,这些动作在点击弹窗左下角按钮时,会触发这个容器中的“动作”。每个弹窗都有一个默认的“动作”,就是“Zoom-To”缩放功能,就是一个小放大镜。
看第一个例子的结果:
按下弹窗中的小测距尺子按钮后,灰色文字条中就出现了这个红色线要素的长度:11.82miles。
看第二个例子的结果:
按下按钮后就会弹出谷歌搜索的结果。
来看代码解析吧!
【Part I 简单action:测距】
给出引用
1 | require( |
多了个geometryEngine,暂时不管它是干啥用的,继续往下看。
事先说明一下,CDN的引用又多了一行:
1 | <link rel="stylesheet" href="https://js.arcgis.com/4.2/dijit/themes/claro/claro.css"> |
函数参数骨架
1 | function(Map, FeatureLayer, MapView, geometryEngine){ |
套路依旧,map和view实例化。var的template是PopupTemplate对象赋给了featureLayer的popupTemplate属性,再把featureLayer丢进map中。
好了,其余陌生的是什么?measureThisAction这个对象、measureThis这个方法、和view.popup.on方法?
别急,一个一个来。
measureThisAction出现的地方一一揪出来:
1 | var measureThisAction = { |
1 | var template = { |
OK了,很明显,measureThisAction是一个Object,有title、id和image,分别对应标题、ID和按钮上的图案。这个png文件在html的同级别目录下。
下面这个template是featureLayer的PopupTemplate属性所需的对象,有title、content,第三个actions属性就将上方的measureThisAction赋给了它(数组形式,因为是Collection类型)。
可以理解如下:
measureThisAction是一个“动作”的声明,本身无功能,可以说是一个UI层上的描述。
————
那么measureThis()这个方法呢?
1 | function measureThis() { |
这里出现了新玩意儿:popup类的selectedFeature属性以及其子属性geometry/attributes,geometryEngine类的geodesicLength方法。
查询API,解读一下:
selectedFeature属性类别:Graphic类,当前选择的要素。
所以Graphic的geometry和attributes属性就容易查到:
geometry:几何体,没什么好说的。类型:Geometry类。
attributes:要素的字段名和字段值的成对集合。
————
geodesicLength():计算传入几何体(Geometry)的长度。
于是这个方法就是:获取几何体,然后把长度用geodesicLength()计算出来,单位是miles。然后设定popup的content动态设置为获取的要素长度值。
————
那么view.popup.on()这个方法呢?
看看完整的方法体:
1 | view.popup.on("trigger-action", function(event) { |
已经灰常、灰常灰常明显了,在popup这个类的“trigger-action”事件上,绑定一个事件方法体:如果“触发”的“动作(action)”的id是“measure-this”,那么执行measureThis()方法执行测距并输出到popup的content上。
于是,这个trigger-action是什么?
trigger-action是popup的事件的一种,和普通的click事件是一样的,意义就是popup的“当动作(action)被触发时”。click即“当鼠标点击时”。
而Popup的triggerAction()方法则接受一个索引,去触发trigger-action这个事件,然后执行索引对应的action。
现在明白了吧!
总结一下。
想要自己弄个按钮在弹窗上,就要:告诉AJS我要创建一个按钮,这个按钮能做什么事情。
第一步,创建一个按钮,使用Object对象measureThisAction来创建,并添加到PopupTemplate的actions属性中(数组形式)。
第二步,写出这个按钮的事件方法measureThis,然后把它绑定到trigger-action事件上。
【Part II 为按钮定制更高级的独特的功能】
节约篇幅,引用和函参骨架一起给出
1 | require( |
鉴于JavaScript的语法特性及面向对象的特性,Part I的很多对象、方法参数都直接用{}赋值了。
可以看到仍然是map和view的实例化,用的也是featureLayer和actions,actions不是[对象名]而是[{}]这种写法给定,不过并没有什么实质性的区别。
然后把featureLayer添加到map中去。
最后和Part I就有所不同了,Part I是view.popup.on(“trigger-action”, function(event){…})
而这里Part II,则是在view的回调函数中先获取popup,然后使用popup.viewModel.on(“trigger-action”, function(event){…})
我们暂时不看这有什么区别,先看看这个function(event){…}做了什么:
1 | popup.viewModel.on("trigger-action", function(event) { |
仍和Part I没有什么区别,同样是获取一些信息,若info存在则直接打开,若不存在则到Google上搜索这个关键词,即如果选择的啤酒店有网站链接,那么就跳转到这个网站;如果啤酒店没有网站,就给出谷歌的搜索页面。
所以我觉得,popup.viewModel.on()的写法和view.popup.on()的写法没什么不同,多一层引用而已。
我们最后到官方的例子中看看有什么遗漏的信息:
没有。说明这两个写法应该是通用的?留个标记,以后测试。
至此,第五章也结束了学习,这一章是我比较忙的时候写的,可能读起来比较费劲,各位看官还请见谅。
在下一章”Searching”即空间查询中,我将转换一下心情,写出更好的笔记来学习AJS 4.2。
空间查询的代码量会激增,而AJS 4.3发布也快了,所以学习AJS 4.2的时间也不多了,新版本出来必定有新特性,我也会保持跟进继续更新4.3的新特性,并予以解读、测试。
下一章再见!
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399654.html
先看结果截图吧(不看过程可以直接看总结,在文末):
随便点击了两个城市斑块,出现结果如图。
我来解读一下这结果和以前的有什么不同:
这个例子使用了PopupTemplate,数据是Layer(使用PortalID创建的Layer)。
但是我们看到图中有个地方不同:当Percent change is这个东西是负数的时候,就是红色+红色下箭头,反之则是绿色字+绿色上箭头。
实际上,这两个箭头是两个png图片。
见html同目录下的两个文件:
所以说,这种特定格式,能随着数字正负自动判断使用什么图片、什么样式的content是怎么实现的呢?
解读开始。
1 | require( |
1 | function(Map, MapView, Layer, dom, number, on){ |
省略了部分代码。在Layer.fromPortalItem返回的Promise对象中,使用了then()方法回调一个函数,操作此Promise返回的layer对象。
首先使用map的add()方法添加layer到地图中去。
然后是定义一个popupTemplate,并赋给layer的popupTemplate属性。
到这里,都很正常,问题是,
到现在为止都没说那个自定义的content是怎么弄的?后面的populationChange方法又是干什么用的?
直接看下文代码块中的content属性:
1 | var popupTemplate = { |
可以看到,有5个{}在content中,也就是说有5个值是动态变化的,在弹窗时会改变。
前4个使用的格式是NumbreFormat,当然这个是可以指定一个方法给它的,
就是第5个{}中的POP2013字段,它的格式就指定为了populationChange方法。我们来看看populationChange方法:
1 | populationChange = function(value, key, data) { |
result中有一个三元判断符 “A?B:C”,意思是若A为真,则选择B作为结果,否则选择C。
看样子就知道diff就是变化数了,它>0就”up.png”,否则就”down.png”。
返回一串html代码,看就知道是什么了,这与我们在开头看到的例子的结果一致。
这个方法的参数中data即各个字段的集合。
popupTemplate的content中如何用自己的规则去控制样式?
这样即可对变化值进行格式自定义控制:
content: “…{字段:Function名}…”
在代码后补全同名方法即可。
就是这么简单!熟悉html组织文本样式的童鞋就能创造更多好看复杂的样式了。
给出源代码:
1 | <!DOCTYPE html> |
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399651.html
这一节我们来看看弹窗的位置和弹窗上能放什么。
先一句话总结:
位置:可以随便(点击时出现或者一直固定在某个位置),也可以指定位置
能放什么:四种,文字、媒体(图片等)、表格、附件。
【Part I 位置】
这一例子和下一个例子中,CDN的引用多了一行
1 | <link rel="stylesheet" href="https://js.arcgis.com/4.2/esri/css/main.css"> |
我也不知道多出来这个claro.css是干嘛的,不过还是注意的好。
这个例子官方给的很复杂,尽管实现上很简单。从html代码开始看看吧!
1 | <body> |
可以看到有一个select选择表单,内含7个可选项,第一个被定义为默认选中的项目。每个option标签的value对应PopupTemplate的position的值。
引用
照常,给出引用。
1 | require( |
好的,没有疑问。下一个。
函数参数骨架
1 | function(Map,MapView,WebMap,dom,on){ |
好的,没有疑问,下——好吧,有疑问,这个popup是什么?就是view实例化后的内置默认弹窗对象啊!(看我上一篇随笔)
——既然说到这个就贴多一部分代码。
1 | var view = new MapView({ |
对,就是MapView的实例化代码。
其他的地方没什么不同,就popup这个不同,与上一节方法略微不同,上一节说的是先声明一个匿名对象,而这里直接把匿名对象在构造函数里写上了。
dockEnabled就是是否能停靠在页面旁边。
dockOptions是Object类型,这里写了buttonEnabled和breakpoint两个属性。意义分别为“停靠按钮是否隐藏”、“是否在点击的地方显示”(后一个参数不确定是什么意思,我猜的,英文水平不是很好哈哈)
view.then()这个方法见我第七篇随笔。
总之,数据(用的是WebMap)和视图都准备好后,就要在then()方法里设置相关的操作了。
then()方法回调函数
1 | view.then(function(){ |
整体大概是上面的样子。
解读如下:
首先,获取view的中央位置。
然后,打开view的默认弹窗。
之后,默认弹窗监听自己的currentDockPosition属性,如果currentDockPosition属性发生更改,就重新打开一次弹窗。
最后获取下拉可选框里的值,把popup的position属性更改。
——————
是不是很乱?
我也觉得很乱。
实际在页面操作的结果如下:
刚打开的页面是这样的:
中间的下拉框选择控件是html组织的,并不是AJS的widget;右上角是“buttonEnabled: false,breakpoint: false”+默认position的结果。
当我切换中间下拉框的选项时,如:
默认的弹窗的位置就发生了变化。这里,是触发了上面代码中“on(selectNode,”change”,function(e){…});”这段代码。
而点击视图任意位置时,如:
view的弹窗的内容(content)就发生了变化(至于popup的content属性是怎么更改的,不知道,代码里没有,可能是WebMap的特殊性吧)
好了,官方绕了这么大一圈就是想说:
要想改popup的位置,只需更改dockOptions这个属性下的position属性即可。
目前只支持上下×左中右配对的6个位置。
所以各位看官看到这里知道原理就行啦,给出on(selectNode,”change”,function(e){…});这段完整的代码,就更加清晰了。
1 | on(selectNode, "change", function (e) { |
源代码也给完:
1 |
|
记住记住关键是popup.dockOptions的position属性即可(规定好的6种)!
【Part II 能放什么】
这个例子数据又切换回了Map而不是上面的WebMap。
这个例子用的是PopupTemplate而不是Popup,所以数据用了featureLayer。
所以,给出预览图:
哇!弹出窗的内容丰富了有木有!
其实就是Popup的content的内容可以定制而已!(上一篇已经着重说过,PopupTemplate这个类的content和Popup的类有点不同,PopupTemplate这个类的content可以是string,也可以是Object[]。
这就有意思了,Object类型的数组就可以指定很多东西给它。
给出featureLayer的声明骨架代码:
1 | var featureLayer = new FeatureLayer({ |
可以看到content是Object[]类型的了,具体代码是:
1 | content: [ |
一颗很庞大的树,缩起来就成了:
四个红框框,看type和对应的信息即可。
第一个是fields类型的,所以就是fieldInfos,再往下展开设置输出文本的格式等等即可。(表格,数据源自PopupTemplate.fieldInfos属性,如果没有就要先进行设置,本例中featureLayer来源自ESRI的服务器,应该是有的)
第二个是text,文本类型的,格式在这截图可以看到。
第三个是media,所以就是mediaInfos,往下还能再分pie-chart(饼图)、image(图片)等等。
第四个是附着物,就是附件,提供下载功能,如PDF文档。
media的类型可以是:image | pie-chart | bar-chart | column-chart | line-chart
再往下就不细看了,需要什么就对应找到PopupTemplate的参考文档即可。下面给出超链接
(发文时是4.2,如果有更新请自行寻找4.2的SDK中的API Reference)
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399632.html
上一篇文章中讲到Popup是一个弹窗,是View对象的默认内置弹窗,并且在View对象构造时就顺便构造了。
那么这个PopupTemplate是什么呢?
后半截单词Template是“模板”的意思,我最初理解就是可以定制的弹窗。仔细阅读API ref后给出更为准确的定义:
PopupTemplate是一个针对Layer和Graphic的弹窗,它与Popup最大的不同的是作用对象不同(Popup主要是针对View)。它服务的对象是Layer中的要素类或者Graphic,它能从要素中获取字段,并用HTML的语法显示到弹窗的content属性中去。
大白话说,PopupTemplate定制程度更高,很多时候复杂的数据显示都要依靠PopupTemplate而不是简单的Popup。
查询得知,PopupTemplate的属性比较少,但是保留了Popup的重要的属性,如
actions、content、title
惊奇的发现,PopupTemplate的title和content有别于Popup的title和content,前者的title和content除了string类型外,还可以是一个方法,content更提供了Object[]的支持。这点很重要,对于后面要介绍的PopupTemplate with Function有关,这里先打个招呼,以后会详细介绍的。
在这个例子中,数据是基于FeatureLayer的。不认识FeatureLayer无妨,就当是桌面版的FeatureLayer即可,拥有简单的要素类和字段等。
给出引用
1 | require( |
函数参数
1 | function(Map, MapView, FeatureLayer){ |
抽出主干内容后,这个示例代码就显得十分简洁:
照例实例化Map和MapView
var一个template匿名对象,这个就是PopupTemplate对象
new一个FeatureLayer
把featureLayer添加到map中
最关键的部分就是template这个匿名对象的内容和featureLayer这个对象如何与template绑定了,其他都不是难事。
template对象是怎么组织的呢?
仍然是抽取主干,看看template有些什么:
1 | var template = { |
显而易见,就是PopupTemplate的几个关键属性:title、content、fieldInfos
前两个是标题和内容。在content里,就用HTML的语法显示了“template”的威力:
1 | content: "<p>As of 2015, <b>{MARRIEDRATE}%</b> of the population in this zip code is married.</p>" + |
熟悉html语法的同学一定不陌生这几个标签是什么含义。但是——
{MARRIEDRATE}、{NEVMARR_CY}、{DIVORCD_CY}还有上面title的{ZIP}是什么鬼?
原来,这个就是featureLayer的各个字段名,当弹窗的时候,对应的字段值就会替换这个大括号。
那么fieldInfo里的Object数组又是什么?
1 | fieldInfos: [ |
这里的//同上是完全相同的意思,为了省略就没有再写一次。
看得出,每一个Object里都含有fieldName,看来是和featureLayer的字段的字段名是对应的了。
而format,即格式,内含输出到弹窗的数字格式,其中digitSeparator就是分隔符,如这样的数字“10,000,000”,逗号就是分隔符;places就是小数位数。
所以这个fieldInfo也是描述数据及其格式的一个属性。
那么,template这个PopupTemplate对象(匿名的)如何与featureLayer绑定呢?
弹窗与要素图层绑定
其实,featureLayer里有一个属性,叫popupTemplate。这就很容易懂了吧?上代码:
1 | var featureLayer = new FeatureLayer({ |
其他什么的先不管,featureLayer这个对象绑定PopupTemplate的方法就是将template对象之间赋值给featureLayer的popupTemplate属性。
好了,我们来看看结果:
结果很明显了,刚刚设置的所有格式都在弹窗上有对应的显示。
MapView中多了一层矢量的图层,这是featureLayer。(看得出ESRI对矢量图形的描边算法还不是很好,尖锐的地方处理的不如桌面版的好,应该是缓冲区算法出的毛病)
总结一下。
给特定的要素图层或者图形设置定制好的弹窗步骤如下:
实例化map和view对象
创建PopupTemplate对象,可以匿名创建也可以new创建
创建featureLayer时在构造函数中把popupTemplate属性赋值即可。
map中添加featureLayer
over~!
在做项目时,遇到了有时地图不能拖动的问题,一开始以为是ArcGIS for JavaScript使用不当造成的,连调好长时间,发现代码并未有问题。加断点,写LOG,查看DOM触发时的代码走向。发现在绑定的函数中的LOG打印了多次,这很不正常啊。
思考出现多次的原因:1)DOM重复创建了;2)监听是不是多绑定了。
针对两个问题,检查发现确实是多次绑定导致的,首先想到的是方案一,查完资料找到了方案二。
方案一
最简单的方法就是放在外围,不用每次都设置监听,这样做的好处是不用每次都去绑定,只在初始化时完成监听,增加了初始化时的运算量。
方案二
冲了会浪,找到另一个解决办法,这样可以根据什么时候需要设置监听而随时绑定,方法接着往下看~
问题示例:
1 | function doSomeThing (e) { |
上面的代码会在触发时执行两次,并不是我们想看到的,我们本想预料的是执行一次绑定的函数,完成时间监听的闭环。
问题永远不会偶然并单独出现,你遇到了,别人肯定也会遇到,方案也会有很多,以下是别人的解决方案。
1 | function doSomeThing (e) { |
在接收监听函数时使用实名函数,不要使用匿名函数,不然都没办法取消事件监听。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399628.html
看本文前最好对第二章(Mapping and Views)中的Map和View类有理解。
视图类有一个属性是Popup类型的popup,查阅API知道这个就是视图的弹窗,每一个View的实例都有一个popup。
这个popup属性在View对象实例化的时候就实例化了的,即随着View的出生,它也会出生,它拥有默认的样子,它显示的文字也是默认的样式。
我们看看Popup这个类:
直接继承自Accessor,位于widgets模块下,说明Popup(弹窗)也是小部件的一种。但是为什么要单独拿出来讲呢?可能用法上比较复杂吧。
如果用户对弹窗有更高的样式要求,官方的说法是
可以自己new一个Popup对象,替换掉view默认的popup即可。
其实Popup有个兄弟类,叫PopupTemplate,它长得很像Popup,但是在功能上更服务于Layer和Graphic,即地理数据,而且也是高度可自定义的,在下一节会细说这两个的区别。
说完了Popup这个新玩意儿,我就来说说第一个例子吧!
它的功能就是点击View上的一个地方,就会弹出一个小窗,显示经纬度和其他信息。
引用
直接给:
1 | require( |
函数参数中因为Map和View 对象是经常性出现的,所以就用 var map = new Map(…);代替了(以后也是)
所以函数参数的关键代码是:
1 | function(Locator, Map, MapView){ |
locatorTask是一个Locator类的对象,用于定位服务。这个不用知道太详细,因为本例中重点并不是它。
重点应该是view的事件“click”,写法同上,但是事件的方法体没有写完整。
我们就来看看这个事件的方法体具体是什么:
1 | function(event){ |
我们看到了,从click事件的event参数中获取到了lat和lon,即经纬度,用Math.round方法对数字进行了一定的处理。
然后关键的一句:
view.popup.open({…});
我们先不说这个是什么,但是肯定是一个方法。
下面的then()方法,是Locator对象的locationToAddress方法的Promise返回对象的回调,用于获取地址成功后把地址显示到popup上。
关键的一句:
view.popup.content = address;
在下面otherwise()方法也有类似的。
现在,我们转到Popup这个类的定义。
Popup类
继承自Accessor类
主要属性:actions(Collection类型)、content(String或Node类型)、location(Point类型)、title(String类型)…
主要方法:open()、destroy()…
看得出,上面的代码使用了open方法,content属性、location属性、title属性。
open方法会把popup的visible属性改为true,然后显示到指定的位置:location。
是不是很简单呢?
更深一层的理解,既然能open,那么肯定是有这个实例的,更说明了popup这个属性是一个对象,在View实例化的时候就完成了实例化。
对其content、title属性进行设置就可以在弹出窗中看到想要的内容和标题了。
总结一下。
View对象自带Popup实例,并随着View对象实例化而实例化。
Popup使用open()方法显示出来,接受location、title、content等可选参数以指定内容、弹窗点等。
最后给个图:
很显而易见。
给出官方的源代码(没有删除注释)
1 | <!DOCTYPE html> |
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6399346.html
直接跳过第三第四章了,第三章Layer和第四章可视化,怎么说呢,Layer是组织数据的,是Map的属性之一。可视化属于符号化编程,暂时不看。
第五章是对数据、结果的显示,类似于alert()、.NET的MessageBox,弹窗嘛。
官方的解释很清楚了,我翻译过来,再加上自己的一些理解。
Popup,是Accessor的 一个子类,它的实例是弹窗对象。像这样
是不是很熟悉?对,在老版本的百度地图中,也有很多这种弹窗的例子。
这一章比较短,给出预览:
【Get started with popups】
【Get started with PopupTemplate】
前两节是预热,通过例子告诉用户怎么使用 Popup 和 PopupTemplate 这两个直接继承自Accessor的类,去弹出一个窗口,以及简单介绍如何使用这两个类的属性。
Popup作用于View对象,而PopupTemplate作用于一些Layer对象或者Graphic对象。
【Popup dock positions】
这节从名字就知道了,控制弹窗的位置。
【Multiple popup elements】
使用多个弹窗元素。弹窗里可以放置很多种元素,因为弹窗不可能只有文本,还可以有图表、外链等。
还介绍了怎么使用related table(并不知道是什么,等看到源代码就知道怎么回事了吧?)
【PopupTemplate with functions】
PopupTemplate这个类的实例,和functions?
口语点的解释就是:
在Layer或者Graphic的弹出窗(PopupTemplate)中,如果对输出文本(输出信息)有特殊的格式需求,是可以指定一段代码来完成这个格式化过程的。
官方的例子,拿人口变化比率来说明。人口变化这个比率(如下图)
有一个图案,就是红色的向下箭头和红色的数字(如果是增长的就是绿色向上箭头和绿色数字)。
这个就不是官方的格式字符串方法了,所以就要自定义方法去完成这个信息的格式化。
【Popup actions】
给弹出窗添加一些自定义的动作——看到初始弹出窗下面那个放大镜按钮没?这就是说,允许自定义按钮并添加一些功能。
【Custom popup actions per feature】
这个看图就好解释了,字面义就是每个要素(feature)都可以定制功能,图中那个啤酒按钮就可以弹出这个地址的网站(大概)。
重点应该不是这个啤酒按钮的功能了,而是能对feature的定制。这个feature的弹窗和View的弹窗有什么不同呢?到代码里看看吧。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6391517.html
这几个例子是第二章除了入门之外比较简单的几个,就做个合集,把最核心的代码(第二参数)和 引用放上来即可,不作多解释。
2D地图添加指北针widget
2D地图一般修正方向为正北方就需要这个widget。
引用:
1 | [ |
在function(Map,MapView,Compass)实例化Map和MapView后,添加这两行代码:
1 | var compassWidget = new Compass({view: view}); |
即可。有关Compass这个widget的构造函数,可以查阅API了解更多,我感觉不会那么少吧…还有这个ui.add()方法,感觉日后添加widget布局的时候可以仔细学学。
ps:Compass在SceneView中是默认显示的。
视图padding改变
这个几乎没有难度。
给个图预览吧:
本来小岛这种东西就应该在地图的最中央位置,但是由于右边多了一块DIV(宽度320px),所以地图为了避开DIV而对齐就必须把视图的padding值改为320(和DIV一样)。在百度地图旧版本中,就喜欢搞一个面板,用于提供搜索信息的输入框和结果框、筛选按钮等。
直接给出源代码:
1 | require( |
仅仅是在view的实例化中把padding的right改为320,在html中:
1 | <body> |
在style标签中,把sidebar这个div的width设置成了同样的320px。
视图保存
这个就比较有趣了。首先要提供一个输入title的地方,还要有一个保存image的地方。
这个例子,使用了SceneView,SceneView的map属性使用了服务器上的WebScene(专题三维地图)。由于这个比较容易,就忽略了。
1 | <body> |
从html代码可以看出,提供了一个createSlideDiv,内含input和一个button以供输入。
还有一个slidesDiv以供保存img列表。
见下图:
第一步,把这几个div加入到view对象的ui中去。
1 | view.ui.add(["createSlideDiv", "slidesDiv"], "top-right"); |
第二步,在view创建好后,执行以下代码。关于then(),请查看鹰眼一文的末尾。
1 | view.then(function() { |
首先,将slidesDiv置为可视。
第二,若WebScene存在slide,那么就全部装到列表中去。
然后var一个slides变量,获取WebScene实例scene的当前所有的slides。
然后遍历每一个slides成员,对其进行createSlideUI操作(见下,即创建DIV列表里的UI对象)。
第三,调用dojo的on(DOM元素,事件类型,事件方法体),为新增slide的那个按钮添加click事件。
注意Slide.createFrom()方法,它将从view中创建slide。
在这个方法的回调函数中,设置输入框的文本slide的标题。
然后把slide添加到scene这个WebScene中去。
最后调用createSlideUI创建div即可。
1 | function createSlideUI(slide, placement) { |
createSlideUI()方法就是创建DIV了,首先用domConstruct.create()方法创建一个divDOM元素,然后确定位置position,
紧接着在这个div里添加标题div、图片div,最后为这个div添加click事件。
虽然最后一个例子看起来比较长,但是逻辑都很清晰,代码也没什么碎片化,完全可以直接照抄两个代码段。
至此,第二章的学习就差不多了,关于WebScene、WebMap这两个从Map类中派生出来的子类(和Basemap同级),我觉得查阅API更有效果,而且可能是得配合ArcServer才能弄懂的东西,就先不学了。
要说数据准备这块这一章还真没写,所以极有可能在Layers那章才会学到了吧,再不济,百度啊谷歌啊干什么用的?
所以,整理一下所学,开始准备学习Popups(弹出框)、Searching(空间查询)和Analysis(空间分析)吧~
Layers和Widgets两章留给三月份研读。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6391513.html
本例子核心:对MapView对象的map属性值进行替换即可达到更改地图数据的效果。
这个例子用的不是Map对象了,而是用的发布在服务器上的专题地图(WebMap)来加载到MapView上进行显示。
在html标签中,使用了section标签,不过没什么稀奇的,就把仨按钮放一块而已。
先给出预览图
三张专题地图:失踪人口密度分布、难民迁徙路线、2015年欧洲来港者。
这个东西很有用,尤其是在展示同一地区的专题地图的时候,这里也展示了什么叫View,什么叫Map。
因为中心点、比例尺是由View对象管控的,所以改变WebMap这个数据的时候,并不会改变位置和比例尺。
我们来看代码:
给出引用
1 | [ |
很清爽。
既然要用到3个专题地图,那么就创建3个WebMap对象:
1 | function(MapView, WebMap, on) { |
也是很简单。
给定一个webmap的ID字符串数组,每个ID是WebMap的唯一标识符。
然后用Collection对象的map()方法进行遍历操作,对传入的每一个ID,匿名函数返回一个WebMap实例。
如何实例化WebMap,请参考API中WebMap的构造函数。
然后,实例化一个MapView,map属性给定webmaps数组的第一个元素,即第一个WebMap——失踪人口图。
在实例化MapView后,就是给顶头的3个按钮添加事件了。
dojo给DOM元素添加事件还记得吗?就是goTo()动画那篇文章。
基本语法:
on(DOM元素,事件类型,事件方法体);
见下:
1 | on(document.querySelector(".btns"), |
使用css选择器点选,即对类进行选择。btns被选中。
在方法体内,先获取data-id这个自定义属性,进入if判断。
先按data-id选择到序号一致的WebMap,假如data-id=“2”,则选中第三张WebMap。
然后更改view.map属性为选择到的WebMap。
这里,数据就替换完成了。
从var nodes…到for循环体结束,讲的是:
获取全部class为btn-switch的DOM元素。
对这个数组进行遍历操作,若当前点击的div的data-id和遍历到的data-id三等号相同,那么就往这个DOM元素的classList添加active-map。
若不,则移除active-map。
意思就是说,如果点击的div就是当前地图,那么就标记为当前活动的WebMap,否则就不是活动的WebMap。
————————
整个程序就是这么简单,替换MapView对象的map属性值,修改DOM元素的classList和操作DOM元素而已。
给出官方源代码:
1 |
|
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6391509.html
同一份数据不同视图查看可能用的比较少,因为3D视图放大很多后就和2D地图差不多了,畸变很小,用于超大范围的地图显示时有用,很多时候都是在平面地图上进行分析、查询、操作。教学需要可能会对这个有要求?
本文没有深究两个比较复杂的函数,希望有朝一日能读懂吧,对于这种同一个Map对象不同视图的同步显示,建议直接copy后半截内容即可。
鹰眼功能和这个类似,不过鹰眼功能(即上一篇文章)是利用watch()和watchUtils.when()来实现的同步,这个例子又有什么不同呢?探究之。
直接看结果图:
看起来就是两个无边框的DIV,宽度各占50%。
实际上操作的结果就是,在任意一个视图拖动、缩放、旋转视图,另一个也跟着变,几乎就是放大版的鹰眼,也可以说是鹰眼的另一种写法。
给出require的引用:
1 | require( |
嗯?又是watchUtils?看来和监听少不了干系了。
//事实上,本例还是用watch监听和watchUtils监听完成的同步,但是代码难于理解,就没有仔细深究。
看骨干部分:
1 | function(Map,MapView,SceneView,watchUtils){ |
实例化一张地图,实例化两个视图,看来和上一个例子还是有区别的。
对此,博主表示暂时保留原代码。
因为synchronizeView长达60行,这个方法一定不普通。
博主的建议是:
如果需要对同一份底图的2D和3D视图进行同步观察显示,那么请直接把这两个syn方法copy过来,甚至包装成一个方法都可以,传入参数是两个view即可。
有兴趣的同学可以查阅这个类:esri/core/Collection
这两个难于理解的方法体内用到了这个类和其几个方法,如map()方法、slice()方法、concat()方法。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6389054.html
文前说明:关于style就是页面的css暂时不做评论,因为官方给的例子的样式实在太简单了,照抄阅读即可。
这篇文章有着大量AJS 4.x版本添加的内容,如监听watch、Promise对象、回调函数、异步处理等内容,原理性的东西我会在文末解释,各位看官不用担心看不懂,我尽量用通俗的语言解释这些。
惯例,如果不习惯从头看到尾,可以直接跳到后面看总结。
大家应该看过商业地图的缩略图功能吧?以度娘地图为例,在使用街景地图的时候,左下角会出现一个地点一样的2D小地图:
这个就是鹰眼功能的应用,在很多桌面软件中如Erdas、Envi,鹰眼是很常见的。
//如果以下超链接日后更新了4.3或更高版本,请自行寻找4.2的sample配合本文学习~
这次就解读2D overview map in SceneView这个例子。
源代码:点我
其实就是一个2D的MapView在3D的SceneView的显示而已,关键就在数据的同步,官方指出了watch()方法是关键。
话不多说,先上最终效果图:
结构大概就是,大的DIV里放SceneView,小的DIV里放MapView。
小的DIV里又有一个黑色的区域来标识当前SceneView的区域。小的DIV的widgets被移除。
html代码为:
1 | <body> |
老样子,require给出引用(以前都叫第一个字符串数组参数,为了省事,以后直接叫引用了)
1 | require( |
重点应该是:
view的watch()方法、watchUtils的when方法、view的toScreen方法、view的extent属性、view的then方法。
既然有两个view(DIV),那么肯定要有两份map(数据)。
所以第二参数(以前的文章叫函数参数,之后都叫第二参数)先将map和view定义如下:
1 | var mainMap = new Map({ |
mainMap、mainView是3D的,overviewMap、mapView是2D的。
当然,我们看到的2D的小地图是没有放大缩小那些控件的,只需1行代码,就可以置空那些控件。
1 | mapView.ui.components = []; |
查阅API,可以知道ui属性是DefaultUI类,DefaultUI继承自UI类。components是字符串数组,若赋值为空数组则清空。相应的,DefaultUI类有remove和empty方法可以清除控件,就不细说了。
为了便于操作,把当前区域的DIV“extendDiv”的DOM元素获取为变量:
1 | var extentDiv = dom.byId("extentDiv"); |
以上就完成了准备部分。
接下来,数据加载完成后,就要对2D的地图和3D的地图进行“同步”了,需要用到两个view的then方法。
then()方法是Promise对象的特有方法,而Promise是什么暂时无需了解,只要知道在AJS 4.x中Promise是一个很重要的东西。
而且,MapView和SceneView类都继承了Promise类。不仅如此,AJS 4.x中很多方法返回的都是Promise对象。
先看看mainView(3D视图)的then方法看看它做了什么:
1 | mainView.then(function() { |
很好,它接受了一个参数,类型是方法。这个匿名方法干了什么呢?这不就是上一篇文章里说的缩放动画嘛!(goTo)跳过,看mapView(2D视图)的then方法看它做了什么:
1 | mapView.then(function() { |
很长的样子。
我慢慢解释。
仍然是接受一个方法作为参数(为什么then接受的参数那么奇怪?文末会解释的)
//题外话:在javascript里头传函数/方法是很常见的,函数/方法是js的一种变量类型,在C/C++里头可以传递函数指针,在C#里头可以传递委托变量。
这个方法里有两个方法,命名为 updateOverview 和 updateOverviewExtent,我们根据这两个方法把这个then方法的代码拆开看,发现watch和watchUtils.when是跟这两个方法配对的。
即:
1 | //两个视图都与updateOverviewExtent方法绑定 |
查阅API,得知视图的父类Accessor就支持watch方法了。值得一提的是,为了实现监听变化,AJS4.x版本专门提供了watch方法代替了以前的旧方法。
watch的用法是:
对象.watch(“该需要监听的属性名”, 属性变化后需要执行的回调函数);
即某对象监听了它的某个属性后,这个属性一旦发生改变,就会去执行某些代码。
在本例中,需要监听的是两个view对象的extent(范围)属性,一旦extent发生变化,那么updateOverviewExtent()方法就会被执行。
updateOverviewExtent()方法的大概意思就是:获取3D视图的范围->获取2D视图的对角线两个角点->更改2D视图上方的区域框的DOM元素的尺寸属性(top、left、height、width)
光改变区域框是不行的,还要改变2D地图的范围。
1 | watchUtils.when(mainView, "stationary", updateOverview); |
watchUtils这个对象,是位于esri/core/watchUtils模块下的一个类。
它代表的含义是:监听某个对象,当这个对象的某个属性是true时,执行给定的方法。
查阅API得知,这个类提供了when这个静态方法,when方法的意义是:
所以,在本例中,意思就是:
当mainView这个3D视图对象的”stationary”属性是true时,刷新mapView这个2D视图对象。
刷新2D视图对象主要用的是上一篇中说到的goTo()方法,本例只指定了center和scale这两个属性组成的Object匿名对象。
SceneView类的stationary属性是布尔类型的,意义是当前视图是否已经静止(一般视图会由鼠标拖拽或者goTo()方法产生动态效果,一旦停止下来,stationary就会变成true)
总结一下。
这个例子大概思路就是:
·先实例化两个map和两个view,对3D的mainView在创建完成后使用then()方法缩放到指定位置。
·其中,对2D的mapView创建完成后使用then()方法,分别监听两个view的extent属性,还监听3D视图的stationary属性。
·当extent属性发生变化时,2D视图上方范围框先进行变化,然后2D地图紧随变化。
·当3D视图静止下来后,刷新2D视图。
监听还算比较好理解,需要注意的不多,注意到watch和watchUtils.when这两个方法返回的都是WatchHandle对象。待以后研究多了监听后,再仔细看看别的监听方法。
难点就在于then方法。
难点。
then()方法怎么来的?这要从ES6(全名ECMAScript 2015)的新规范Promise对象说起。ECMAScript是JavaScript的标准,JS是ES的实现。
Promise是什么?这个东西说复杂也很复杂,它是:
为了处理异步操作多层回调函数的写法枯燥、难以阅读维护而产生的,由CommonJS社区发起的一个新规范的类。
最显著的特征是它实例化的对象都有then()和catch()方法(PromiseA+规范?好像是)
在AJS中,继承了Promise的类有:
全部的Layers
MapView、SceneView、LayerView
ViewAnimation
能返回Promise对象的类数不胜数。
所以说,为什么要用Promise?
这又要从异步操作说起了。
————
在AJS 4.x中,数据(Map类)和视图(View类)是分开的,3.x版本绘图渲染是Map自己完成的。
由于View视图类被分离开,绘图逻辑就成了它的主要功能。当然,绘图不会很快,往往有一个过程,尤其是超大数据量的绘图的时候会有一个比较长的等待过程。
所以,在JS里,较长的处理会丢给异步处理(就是同时进行好几个操作)
但是但是,我们知道JS是单线程的,它是怎么处理异步处理的呢?简单说说,JS的异步处理其实是个“伪异步”,是先完成同步代码才执行异步代码的。
通常,异步代码会做一些计算量比较大的事情,而同步代码则做一些不怎么耗时间的初始化工作。就是说
同步代码花少量的时间去初始化一些事情,其间有n个异步任务丢给异步队列。当同步代码完成初始化后(时间短),异步代码开始按顺序执行。
比如:界面的构建交给同步代码,而其间有n个后台数据交换、处理、计算的任务,就丢给异步队列去准备。当界面构造好(时间往往很短,几乎是秒速),异步代码就在后面开始执行。
这先看到的界面会让体验好很多,如果异步代码(就是耗时比较大的任务)放在同步代码里执行,那么由于同步的性质,必须等待这些耗时大的任务执行完成才能继续往下走(js的特点,单线程)
【在本例中】
初始化view,我不知道在云端是怎么运行的(因为我用的是CDN来运行AJS程序),但是我知道view的实例化肯定是用了异步操作。
即先完成网页的加载(出现3D地球和2D地图,同步),再进行视图的渲染(山体拔高等,异步)。
sometimes,异步操作当然会有一个结果,比如异步在后面花好长时间算出个矩阵,但是同步代码已经结束了,异步任务丢过去的时候结果还没出来,怎么获取它?
我们可以用一个方法去获取它。这个方法,古时候叫回调函数。
在没有Promise类的时候,通常用回调函数这种办法实现(也能用事件、监听)异步是很正常的一种。
但是当回调函数本身也是个异步操作的时候,就会显得晕头转向。
异步第一层,有结果要用回调函数返回给同步代码->回调函数是第二层,这个回调函数里头需要用二级回调函数返回结果给第一层->……
举个例子:
我是领导,我现在有两件事:有个事儿要做,和喝茶。
这两件事不冲突,虽然这个事儿很无聊,耗时大(如文字录入)。
所以我把这个事儿丢给经理(异步第一层),我继续喝茶(同步)
异步第一层就是经理要做这个事,但是这个事情绝大部分是无聊的,最后的整理比较简单。
所以经理就把这个无聊的部分丢给职员(异步第二层),等待职员把这部分做完的同时,也去喝茶(同步)。
于是,职员的结果就是二级回调函数,职员把结果完成后,“回调”给经理。
经理拿着职员的结果整理好,“回调”给领导。(第二层异步完成)
此时领导茶已经喝完了(同步完成),而任务也完成了(第一层异步完成)。
这里如果用老的写法将会非常的烦,如果用Promise的then写法就是
领导要做事儿.then(function(){让经理去做})
.then(function(){让职工做});
链式写法,简单,容易看,也容易维护。
then里面的function就是回调函数,告诉异步任务完成后,要怎么处理异步结果的一段代码。
最后看看then方法的语法:
then(function resolve, function rejected);
我们一般只用前一个参数,即异步成功要怎么处理。而后一个参数是异步任务处理失败后要做什么。
甚至AJS官方还给出了处理中要做什么的第三个参数…这个就不说那么多了。
——
大概清楚是这么个过程后,我们知道View对象是Promise对象(继承),而且有异步操作的过程。
所以,mainView.then(function(){…});的意义就是
当3D视图在服务器端异步操作成功后,使用goTo()缩放到指定的位置。
文末,我还想说说监听,监听在AJS 3.x版本里是通过事件完成的,而AJS 4.x全新使用了watch一派写法。有关这些可以参考AJS 4.2的Guide文档。
最后的最后,关于异步和回调函数部分我也是学了一天后才给出的模糊定义,希望大家能看懂吧…我也不是很能理解,官方给的多层then()是这样的:
出处:点我
then里头当然是方法,无参的。只有子一层的结果完成的时候,父一层的then才能凭借子一层的结果的回调完成异步。
给一些我阅读中觉得不错的对异步、回调函数讲解的文章:
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6388084.html
这是个很有意思的例子,不过例子给的比较复杂,需要查很多API,我会在文章最后给出关键的类和属性解释。
同样发现一个很有意思的事儿:博客园似乎有爬虫,我4号发布的blogs,5号就在百度和google搜索页面上看到了转载或者复制。
这篇文章逻辑组织不太好,想知道怎么做缩放动画的可以直接拉到尾部看结论。
当然,这篇代码比较多,不建议手机看。
进入正题,goTo()动画,官方的例子是在SceneView中实现的。
照例,给出require的第一个字符串数组参数
1 | require( |
除了上一次熟悉的Map类和SceneView类,还多出来了query和on这俩,字面意思可以猜测是查询和事件有关。继续往下看。
为了实现动画移动摄像机,就要在html页面组织一些按钮。
于是,在html的body标签内如下组织:
1 | <body> |
6个按钮,分别是:默认漫游、较慢漫游、较快漫游、渐渐加快漫游、10秒钟漫游、弹性缩放到柏林
于是,在require的第二个函数参数里,就这样给这些button添加事件:
1 | funtion(Map,SceneView,query,on) |
仅仅是一个on(dojo.query(), , function(){})方法即可实现为DOM元素添加对应的事件。这里,指定了“click”事件。
关于dojo.query(),参考博客自 - http://blog.csdn.net/dojotoolkit/article/details/6265337
这里借用了CSS的语法,dojo.query(“#default”),这样就能获取到元素了.
需要注意的是query方法获取到的是数组,如果只有一个那就是它本身。
单个按ID查找DOM元素的方法是dojo.byId()
我们继续。获取html中定义的按钮元素后添加了事件以及函数体后,自然就是为它添加动画效果了。
我们取完整函数体看看,有什么异同。
1 | on(dojo.query("#default"), "click", function() { |
我们可以发现有很多东西是多出来的。以默认漫游按钮为切入点,发现使用了view这个对象的goTo()方法,参数未知,看来是一个有返回值的方法。查看官方API和本例代码得知goTo()方法和shiftCamera()方法的含义:
goTo()方法
将视图转移到给定的目标。参数可以是:Geometry或Geometry数组、Graphic或Graphic数组、Viewpoint对象、Camera对象。
本例中就使用了Camera对象(shiftCamera方法的返回值就是一个Camera)或Object对象(缩放到柏林)。
以上的参数是“target”,即目标。
后面还有一些可选的参数,用{}括起来作为一个Object对象:
animate(boolean)、speedFactor(number类型)、duration(numer类型)、maxDuration(number类型)、easing(string或方法体)
speedFactor是速度因子,很好理解,默认是1.
duration是持续时间,如果有这个,那么speedFactor就会被覆盖。
maxDuration是最大持续时间。
easing是缓动方式。
通常,easing必选,speedFactor和duration、maxDuration三选一。
参数均可选。
shiftCamera()方法
代码如下:
1
2
3
4
5
6 > function shiftCamera(deg){
> var camera = view.camera.clone();
> camera.position.longitude += deg;
> return camera;
> }
>
给定一个deg(旋转角,角度制),camera的position的longitude值加上deg值。当然,deg要和longitude类型一样。
position是一个空间点(Point类,继承自Geometry),longitude是经度。AJS4.2是默认用Web Mercator或WGS 84参考系的。
本例中默认漫游传入了60度,即每次按按钮就会把视角旋转60度。
我们再来看看第2-第5个漫游按钮。
它们除了shiftCamera方法返回的Camera对象(target)外,还多了一个{}Object对象(option)。
本例中,除了弹性缩放到柏林这个按钮外,其余都是用Camera对象和Object对象组合的方式,达到动画效果。
我们当然可以直接用{}来定义一个Camera对象,就像弹性缩放到柏林这个按钮的方法体内写的。
1 | on(dojo.query("#bounceBerlin"), "click", function(){ |
在Object对象中,easing参数可以指定为一个方法体(返回值是number即可)。这里customEasing就是这样的一个方法。(看起来略复杂)
1 | function customEasing(t) { |
(插一句:如果在C#中,可不能随便这样给个方法名就行了,要用委托才能操作方法)
关于easing这个参数的string值,大家可以自行到API查询,我简单列出这几个枚举:
1 | linear, in-cubic, out-cubic, in-out-cubic, in-expo, out-expo, in-out-expo |
都可以自己试试,估计就是速度的不同而已。官方推荐小于1s的动画就用自己定义的方法体,超过1s的就用上面的枚举就行了。
总结。
地图缩放动画的核心就是view对象的goTo()方法的使用。
goTo()方法在MapView类和SceneView类中都有提供,但是在它们的父类View类中没有。
本文就对官方的API和例子进行学习,主要了解goTo()方法的参数的使用。
用法:view对象.goTo(target, option);
可以是:{定义Camera对象}+{Option参数}传入(前5个按钮)
也可以是:直接传一个Camera对象+{Option参数}(最后一个按钮)。
Option参数中的easing是“必选”的(不然就没动画效果了呀),speedFactor、duration、maxDuration是三选一。
Camera对象可以自己用方法体返回,也可以直接用js的大括号定义。
改变Camera对象的一些属性值,如经纬度,就可以达到改变视角。
至于其他的,如Geometry、Graphic、Viewpoint就没有进行学习了,参考API可以解决,本文只是解读官方的例子达到入门效果。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6363915.html
内容如上,截图自ESRI官网,连接:ArcGIS API for JavaScript 4.2(可能会跳转到4.3或者更高最新版本,如果有需要,到CSDN可以下载到4.2的离线文档)
【Get Started】
类似于绪论一样的东西,抽取了最需要关注的几个例子。如:加载Map和View,加载layers,使用弹出窗口,视觉化,与使用窗口小部件。(wtf居然没有分析你想搞事情啊web除了展示难道不应该有()&&*……@)好吧,4.2刚出的时候分析功能确实不太全。
最基础的,知道地图和视图的区别,能使用2D和3D地图,对地图的布局有一定的了解,对地图的动画也有小小的使用示例。还介绍了2D3D视图同步、鹰眼、指北针等功能。这一章是最基础的了,其中第一小节介绍了入口函数的参数意义。
【Layers】
这一章很庞大,和地理数据有关。按顺序读下去:
支持多图层叠加显示
支持要素图层、场景图层(直接把影像拔高,推出了一个盒子形状的立体物件的场景图层)、矢量瓦片图层(这个很厉害啊)、影像图层、流图层、CSV图层、OpenStreet地图图层、Web瓦片图层、点云图层(这个更厉害1.1亿的激光雷达点云数据都能加载进来)
其中,对MapImageLayer和ImageryLayer有着重的介绍,前者不知道是什么东西,后者是影像图层(栅格图层)。这也是数据中最关键的了。
最后我想问一下:图层这种东西,是怎么创建的(查API应该可以查到如何创建实例)?数据又如何从硬盘中获取?需要服务器环境吗?(尚待解决)
回答上一句提到的问题。图层通过ArcGIS Server发布的各种服务创建,也可以通过Online或者Portal上的ID创建。
【Visualization】
这一章是符号的定制和色彩、样式的管理,主要是视觉方面的工作,在Web上不可能弄得很丑,Web最重要的功能就是“演示”,可视化这章就是为此而生。这一章内容繁多但是不难,简单看过去就是自定义符号、色彩设置、色带设置等,和桌面版的差不多。短时间内没法看完,以后待功能性章节完成学习后再进行攻读。
Popups意思为弹出窗口,第一个就以坐标的显示为例子。
弹出窗口也是一个相当基础的功能,因为有的查询功能的结果就需要用这种输出方式。
紧接的是:使用弹出小窗口的模板、对popups的位置进行固定(Dock,上下左右相对固定)
然后是在popups上加需要的内容,官方演示了表格、多媒体、文档和图表。
最后,在弹出窗口上也是可以添加按钮的(官方演示了Zoom功能和测量功能),也可以定制自己的功能(跳转到别的地方什么的)
【Graphics】
这章简单,和GDI差不多的东西,往视图中添加图形。支持2D和3D视图添加。
这章就是空间查询了。不过是基于widget的查询,底层似乎没给出。
使用Search widget可以查询,这功能在国内的地图厂商上都有,并且他们很像。不同的是,这是WebGIS,而不是商用GIS,功能是可以自己定制的。
当然,也可以对3D、2D地图进行筛选查询、点击查询。
ESRI也提供了最古老的方式——在表格上显示查询结果。
这一章是空间分析,虽然不及Desktop上的ArcMap和ArcGIS Pro那么强悍,不过对于基础的业务倒也足够。
ESRI已经说了,空间分析仍会在未来的版本中发布更新以与3.x产品线追平。
WebGIS的空间分析主要是:缓冲区分析和网络分析。例子中的网络分析是求解最短路径,比桌面版的操作友好多了(见本人的ArcGIS 网络分析专辑)。
此外官方还提供了:可视域分析、热点分析、查询高程(点高程变化和线路海拔变化等)。
【Widgets】
这属于UI的定制,大布局上可以用第三方JS库,在地图上的UI控件就交给ESRI吧。
粗看下去,主要有:
Home按钮控件、图层列表控件、图例控件、定位控件、地图打印到本地磁盘控件、追踪定位控件(平面的)、导航定位控件(用于导航)、快速布局控件(widget太多了可以用一个东西管理,view对象的ui属性)
ESRI强大到:widget似乎是可以自定义的(支持第三方JS库!如Angular、React),我看到了custom的字样,但是没有仔细往下看,因为提供的控件和功能已经满足大多数的需求了。甚至,widget还可以给它弄个皮肤。
颤抖吧人类。
【More 3D】
这章跟数字高程模型有关,能使用大地高程进行3D可视化,并控制摄像机的位置和角度。
同时,也可以把高程信息叠加到要素类上,进行高程3D显示。
我还看到了一个好玩的东西:控制太阳(就是控制日照角度,根据时间),其实就是环境的控制。
AJS的3D引擎是可以拓展的,甚至提及了Threejs公共3D引擎库(Threejs差评,文档忒少难读)。
ESRI提供了WebGL的检测功能,因为3D的支持需要WebGL,如果浏览器不支持,那肯定是不行的。
【Other】
这里是一些杂项,如从服务器上获取资料、许可,连接服务器等。
API从esri这个大类分下去,有以下一级模块及与一级模块并列的类:
类:Basemap、Camera、Color、config、Graphic、Ground、kernel、PopupTemplate、request、Viewpoint、WebMap、WebScene
一级模块:/core /geometry /identity /layers /portal /renderers /support /symbols /tasks /views /webmap /webscene /widgets
一些重要的二三级模块展示如下
/core: /accessorSupport /workers
/geometry: /support
/layers: /support
/renderers: /smartMapping/statistics /smartMapping/symbology /support
/symbols: /support
/tasks: /support
/views: /3d /layers /ui
/widgets: /support
对于API Reference,等用到一定时候会做类图,未完待续。其实官方的API已经很不错了,但是就是没有中文版,需要用过的人去做一些工作。
最后,善用API Reference。
本系列是转载至博客园原创作者-秋意正寒-致敬!
原地址:https://www.cnblogs.com/onsummer/p/6363914.html
目录如下(点击即可超链接跳转):
鹰眼 【重要】
2D3D视图同步 【重要】
同视图不同数据同步 【重要】