///////////////////////////////////////////////////////////////////////////
// Copyright © 2014 - 2018 Esri. All Rights Reserved.
//
// Licensed under the Apache License Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///////////////////////////////////////////////////////////////////////////
define([
'dojo/on',
'dojo/Evented',
'dojo/_base/declare',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
'dijit/_WidgetsInTemplateMixin',
'dojo/store/Memory',
'dojo/Deferred',
'dojo/store/Observable',
'dijit/tree/ObjectStoreModel',
'dojo/promise/all',
'dojo/_base/lang',
'dojo/_base/html',
'dojo/_base/array',
'jimu/utils',
'jimu/dijit/_Tree',
'jimu/LayerInfos/LayerInfos',
'jimu/dijit/LoadingIndicator'
],
function(on, Evented, declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Memory, Deferred, Observable,
ObjectStoreModel, all, lang, html, array, jimuUtils, JimuTree, LayerInfos, LoadingIndicator) {
var LayerChooser = declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, Evented], {
templateString:'
' +
'
' +
'' +
'' +
'${nls.noLayersTip}' +
'
' +
'
',
_store: null,
_id: 0,
_treeClass: 'layer-chooser-tree',
//constructor options:
createMapResponse: null, //The response of method createMap.
multiple: false, //Can select multiple layers or a single layer.
onlyShowVisible: false,
updateWhenLayerInfosIsShowInMapChanged: false,
onlyShowWebMapLayers: false,
displayTooltipForTreeNode: false,
//public methods:
//getSelectedItems
//methods need to override:
//getSelectedItems
//filter
//attributes:
//tree
//events:
//tree-click
//update
postMixInProperties:function(){
this.nls = window.jimuNls.basicLayerChooserFromMap;
},
postCreate: function() {
this.inherited(arguments);
html.addClass(this.domNode, 'jimu-basic-layer-chooser-from-map');
this.multiple = !!this.multiple;
this.shelter = new LoadingIndicator({hidden:true});
this.shelter.placeAt(this.domNode);
this.shelter.startup();
this._createTree();
this.basicFilter = lang.hitch(this, this.basicFilter);
this.filter = LayerChooser.andCombineFilters([this.basicFilter, this.filter]);
if(this.createMapResponse){
this.setCreateMapResponse(this.createMapResponse);
}
},
basicFilter: function(layerInfo){
var def = new Deferred();
if(this.onlyShowVisible){
def.resolve(layerInfo.isShowInMap());
}else{
def.resolve(true);
}
return def;
},
//to be override, return Deferred object
//if resolve true, means layerInfo can be displayed in tree
filter: function(layerInfo){
/*jshint unused: false*/
var def = new Deferred();
def.resolve(true);
return def;
},
//return an array, each element has 'name' and 'layerInfo' attribute
getSelectedItems: function(){
var items = this.tree.getSelectedItems();
var handledItems = array.map(items, lang.hitch(this, function(item){
return this.getHandledItem(item);
}));
return handledItems;
},
//return an array, each element has 'name' and 'layerInfo' attribute
getAllItems: function(){
var items = this.tree.getAllItems();
var handledItems = [];
array.forEach(items, lang.hitch(this, function(item){
if(item.id !== 'root'){
var handledItem = this.getHandledItem(item);
handledItems.push(handledItem);
}
}));
return handledItems;
},
//to be override
getHandledItem: function(item){
return {
name: item.name,
layerInfo: item.layerInfo
};
},
_isLeafItem: function(item) {
return item.isLeaf;
},
setCreateMapResponse: function(createMapResponse){
this.createMapResponse = createMapResponse;
var map = this.createMapResponse.map;
var mapItemInfo = this.createMapResponse.itemInfo;
LayerInfos.getInstance(map, mapItemInfo).then(lang.hitch(this, function(layerInfosObj) {
this.layerInfosObj = layerInfosObj;
this.own(
on(this.layerInfosObj, 'layerInfosChanged', lang.hitch(this, this._onLayerInfosChanged))
);
if(this.updateWhenLayerInfosIsShowInMapChanged){
this.own(
on(this.layerInfosObj, 'layerInfosIsShowInMapChanged',
lang.hitch(this, this._onLayerInfosIsShowInMapChanged))
);
}
this._buildTree(this.layerInfosObj);
}));
},
_onLayerInfosChanged: function(layerInfo, changedType) {
/*jshint unused: false*/
this._buildTree(this.layerInfosObj);
this.emit('update');
},
_onLayerInfosIsShowInMapChanged: function(changedLayerInfos){
/*jshint unused: false*/
this._buildTree(this.layerInfosObj);
this.emit('update');
},
_buildTree: function(layerInfosObj){
this._clear();
html.setStyle(this.errorTipSection, 'display', 'block');
var layerInfos = [];
if(this.onlyShowWebMapLayers){
layerInfos = layerInfosObj.getLayerInfoArrayOfWebmap();
layerInfos = layerInfos.concat(layerInfosObj.getTableInfoArrayOfWebmap());
}else{
layerInfos = layerInfosObj.getLayerInfoArray();
layerInfos = layerInfos.concat(layerInfosObj.getTableInfoArray());
}
if(layerInfos.length === 0){
return;
}
html.setStyle(this.errorTipSection, 'display', 'none');
array.forEach(layerInfos, lang.hitch(this, function(layerInfo){
this._addDirectLayerInfo(layerInfo);
}));
},
_addDirectLayerInfo: function(layerInfo){
if(!layerInfo){
return;
}
layerInfo.getLayerObject().then(lang.hitch(this, function(){
this._addItem('root', layerInfo);
}), lang.hitch(this, function(err){
console.error(err);
}));
},
_clear:function(){
var items = this._store.query({parent:'root'});
array.forEach(items, lang.hitch(this, function(item){
if(item && item.id !== 'root'){
this._store.remove(item.id);
}
}));
},
_addItem: function(parentId, layerInfo) {
var item = null;
var layerTypeDef = layerInfo.getLayerType();
var validDef = this.filter(layerInfo);
all({
layerType: layerTypeDef,
valid: validDef
}).then(lang.hitch(this, function(result) {
if(result.valid) {
var callback = lang.hitch(this, function(isLeaf, hasChildren){
this._id++;
item = {
name: layerInfo.title || "",
parent: parentId,
layerInfo: layerInfo,
type: result.layerType,
layerClass: layerInfo.layerObject.declaredClass,
id: this._id.toString(),
isLeaf: isLeaf,
hasChildren: hasChildren
};
this._store.add(item);
});
var subLayerInfos = layerInfo.getSubLayers();
var isLeaf = subLayerInfos.length === 0;
var hasChildren = true;
if(isLeaf){
hasChildren = false;
callback(isLeaf, hasChildren);
}else{
var defs = array.map(subLayerInfos, lang.hitch(this, function(subLayerInfo){
return this.filter(subLayerInfo);
}));
all(defs).then(lang.hitch(this, function(filterResults){
var hasChildren = array.some(filterResults, function(filterResult){
return filterResult;
});
if(hasChildren){
callback(isLeaf, hasChildren);
}
}));
}
}
}));
},
_getRootItem:function(){
return { id: 'root', name:'Map Root', type:'root', isLeaf: false, hasChildren: true};
},
_createTree: function() {
var rootItem = this._getRootItem();
var myMemory = new Memory({
data: [rootItem],
getChildren: function(object) {
return this.query({
parent: object.id
});
}
});
// Wrap the store in Observable so that updates to the store are reflected to the Tree
this._store = new Observable(myMemory);
var myModel = new ObjectStoreModel({
store: this._store,
query: {
id: "root"
},
mayHaveChildren: lang.hitch(this, this._mayHaveChildren)
});
this.tree = new JimuTree({
multiple: this.multiple,
model: myModel,
showRoot: false,
isLeafItem: lang.hitch(this, this._isLeafItem),
style: {
width: "100%"
},
onOpen: lang.hitch(this, function(item, node) {
if (item.id === 'root') {
return;
}
this._onTreeOpen(item, node);
}),
onClick: lang.hitch(this, function(item, node, evt) {
this._onTreeClick(item, node, evt);
this.emit('tree-click', item, node, evt);
}),
getIconStyle: lang.hitch(this, function(item, opened) {
var icon = null;
if (!item || item.id === 'root') {
return null;
}
var a = {
width: "20px",
height: "20px",
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundImage: ''
};
var baseUrl = window.location.protocol + "//" + window.location.host + require.toUrl("jimu");
var imageName = this._getIconImageName(item, opened);
if (imageName) {
a.backgroundImage = "url(" + baseUrl + "/css/images/" + imageName + ")";
icon = a;
}
return icon;
}),
getTooltip: lang.hitch(this, function(item){
return this.displayTooltipForTreeNode ? item.layerInfo.title : "";
})
});
html.addClass(this.tree.domNode, this._treeClass);
this.tree.placeAt(this.shelter.domNode, 'before');
},
_mayHaveChildren: function(item) {
return item.hasChildren;
},
_getIconImageName: function(item, opened) {
var imageName = '';
if (item.type === 'ArcGISDynamicMapServiceLayer' ||
item.type === 'ArcGISTiledMapServiceLayer') {
if (opened) {
imageName = 'mapserver_open.png';
} else {
imageName = 'mapserver_close.png';
}
} else if (item.type === 'GroupLayer') {
if (opened) {
imageName = 'group_layer2.png';
} else {
imageName = 'group_layer1.png';
}
} else if (item.type === 'FeatureLayer') {
var geoType = jimuUtils.getTypeByGeometryType(item.layerInfo.layerObject.geometryType);
if (geoType === 'point') {
imageName = 'point_layer1.png';
} else if (geoType === 'polyline') {
imageName = 'line_layer1.png';
} else if (geoType === 'polygon') {
imageName = 'polygon_layer1.png';
}
} else if(item.type === 'Table'){
imageName = "table.png";
} else if(item.type === 'ArcGISImageServiceLayer' ||
item.type === 'ArcGISImageServiceVectorLayer'){
imageName = 'image_layer.png';
} else {
if (opened) {
imageName = 'mapserver_open.png';
} else {
imageName = 'mapserver_close.png';
}
}
return imageName;
},
_onTreeOpen: function(item, node) { /*jshint unused: false*/
if(item.id === 'root'){
return;
}
var layerInfo = item.layerInfo;
var subLayerInfos = [];
var defs = [];
subLayerInfos = layerInfo.getSubLayers();
if (item.checked) {
return;
}
this.shelter.show();
defs = array.map(subLayerInfos, lang.hitch(this, function(subLayerInfo) {
return subLayerInfo.getLayerObject();
}));
all(defs).then(lang.hitch(this, function() {
if (!this.domNode) {
return;
}
array.forEach(subLayerInfos, lang.hitch(this, function(subLayerInfo) {
this._addItem(item.id, subLayerInfo);
}));
item.checked = true;
this.shelter.hide();
}), lang.hitch(this, function(err) {
console.error(err);
this.shelter.hide();
if (!this.domNode) {
return;
}
}));
},
//to be override
_onTreeClick: function(item, node, evt){/*jshint unused: false*/},
destroy: function(){
if(this.shelter){
this.shelter.destroy();
this.shelter = null;
}
if(this.tree){
this.tree.destroy();
}
this.inherited(arguments);
}
});
//layerTypes: array, such as ['FeatureLayer']
//supports layers:
// "FeatureLayer"
// "ArcGISDynamicMapServiceLayer"
// "ArcGISTiledMapServiceLayer"
// "GeoRSSLayer"
// "KMLLayer"
// "WMSLayer"
// "WTMSLayer"
// "FeatureCollection"
//the returned filter will filter layers by layerType
LayerChooser.createFilterByLayerType = function(layerTypes) {
if (!lang.isArrayLike(layerTypes)) {
layerTypes = [];
}
return function(layerInfo) {
var defResult = new Deferred();
if (layerTypes.length === 0) {
defResult.resolve(true);
} else {
var layerTypeDefs = [];
layerInfo.traversal(function(layerInfo) {
layerTypeDefs.push(layerInfo.getLayerType());
});
all(layerTypeDefs).then(function(layerTypeDefResults) {
for (var i = 0; i < layerTypeDefResults.length; i++) {
for (var j = 0; j < layerTypes.length; j++) {
if (layerTypeDefResults[i] === layerTypes[j]) {
defResult.resolve(true);
return;
}
}
}
defResult.resolve(false);
}, function(err){
console.error(err);
defResult.reject(err);
});
}
return defResult;
};
};
//the returned filter only filters FeatureLayer
LayerChooser.createFeaturelayerFilter = function(types, showLayerFromFeatureSet, showTable, mustSupportStatistics){
var allTypes = ['point', 'polyline', 'polygon'];
if(types && types.length > 0){
types = array.filter(types, function(type){
return allTypes.indexOf(type) >= 0;
});
if(types.length === 0){
types = allTypes;
}
}
else{
types = allTypes;
}
return function(layerInfo){
var defLayerType = layerInfo.getLayerType();
var defLayerObject = layerInfo.getLayerObject();
return all({
layerType: defLayerType,
layerObject: defLayerObject
}).then(function(result){
var layerType = result.layerType;
var layerObject = result.layerObject;
if (layerType === 'ArcGISDynamicMapServiceLayer') {
return true;
} else if (layerType === 'ArcGISTiledMapServiceLayer') {
return true;
} else if (layerType === 'GroupLayer'){
return true;
} else if (layerType === 'FeatureCollection'){
return true;
}else if (layerType === 'FeatureLayer') {
var geoType = jimuUtils.getTypeByGeometryType(layerObject.geometryType);
var isValidGeoType = array.indexOf(types, geoType) >= 0;
var isLayerValidStatistics = LayerChooser._shouldPassStatisticsCheck(mustSupportStatistics, layerObject);
if (layerObject.url) {
//featurelayer by url
var isLayerSupportQuery = jimuUtils.isFeaturelayerUrlSupportQuery(layerObject.url,
layerObject.capabilities);
return (isValidGeoType && isLayerSupportQuery && isLayerValidStatistics);
} else {
//featurelayer by featureset
return (showLayerFromFeatureSet && isValidGeoType);
}
} else if(layerType === 'Table'){
//if showTable is true, we will ignore types
var isTableSupportQuery = jimuUtils.isFeaturelayerUrlSupportQuery(layerObject.url,
layerObject.capabilities);
var isTableValidStatistics = LayerChooser._shouldPassStatisticsCheck(mustSupportStatistics, layerObject);
return (showTable && isTableSupportQuery && isTableValidStatistics);
}else{
return false;
}
});
};
};
//the returned filter only filters ArcGISImageServiceLayer and ArcGISImageServiceVectorLayer
LayerChooser.createImageServiceLayerFilter = function(isSupportQuery, mustSupportStatistics){
return function(layerInfo){
var defLayerType = layerInfo.getLayerType();
var defLayerObject = layerInfo.getLayerObject();
return all({
layerType: defLayerType,
layerObject: defLayerObject
}).then(function(result){
var layerType = result.layerType;
var layerObject = result.layerObject;
if(layerType === 'ArcGISImageServiceLayer' ||
layerType === 'ArcGISImageServiceVectorLayer'){
if(isSupportQuery){
if(jimuUtils.isImageServiceSupportQuery(result.layerObject.capabilities)){
if(mustSupportStatistics){
return LayerChooser._shouldPassStatisticsCheck(mustSupportStatistics, layerObject);
}else{
return true;
}
}else{
return false;
}
}else{
return true;
}
}else{
return false;
}
});
};
};
LayerChooser._shouldPassStatisticsCheck = function(mustSupportStatistics, layerObject){
if(mustSupportStatistics){
var isSupport = false;
if (layerObject.advancedQueryCapabilities) {
isSupport = !!layerObject.advancedQueryCapabilities.supportsStatistics;
} else {
isSupport = !!layerObject.supportsStatistics;
}
return isSupport;
}else{
return true;
}
};
LayerChooser.createQueryableLayerFilter = function(mustSupportStatistics){
var types = ['point', 'polyline', 'polygon'];
var featureLayerFilter = LayerChooser.createFeaturelayerFilter(types, false, true, mustSupportStatistics);
var imageServiceLayerFilter = LayerChooser.createImageServiceLayerFilter(true, mustSupportStatistics);
var filters = [featureLayerFilter, imageServiceLayerFilter];
var combinedFilter = LayerChooser.orCombineFilters(filters);
return combinedFilter;
};
//combine multiple filters into one filter
//if all filters pass, the combined filter will pass
LayerChooser.andCombineFilters = function(filters){
return LayerChooser._combineFilters(filters, true);
};
//combine multiple filters into one filter
//if one of the filters passes, the combined filter will pass
LayerChooser.orCombineFilters = function(filters){
return LayerChooser._combineFilters(filters, false);
};
LayerChooser._combineFilters = function(filters, isAnd){
return function(layerInfo){
var defResult = new Deferred();
var defs = array.map(filters, function(filter){
return filter(layerInfo);
});
all(defs).then(function(filterResults){
var isPass = false;
if(isAnd){
isPass = array.every(filterResults, function(filterResult){
return filterResult;
});
}else{
isPass = array.some(filterResults, function(filterResult){
return filterResult;
});
}
defResult.resolve(isPass);
}, function(err){
console.error(err);
defResult.reject(err);
});
return defResult;
};
};
return LayerChooser;
});