diff --git a/src/util/ArcGISRest.js b/src/util/ArcGISRest.js
index 9c82bf308..a03229011 100644
--- a/src/util/ArcGISRest.js
+++ b/src/util/ArcGISRest.js
@@ -62,22 +62,27 @@ Ext.define('BasiGX.util.ArcGISRest', {
},
/**
- * Creates the URL for a FeatureServer request.
+ * Creates the URL for a FeatureServer/MapServer/GPServer.
*
* @param {string} serviceUrl The URL of the service.
- * @param {string} serverName The name of the FeatureServer.
+ * @param {string} serverName The name of the Server.
* @param {string} format The output format.
- * @return {string} The URL to the FeatureServer.
+ * @param {string} serverType FeatureServer/MapServer/GPServer.
+ * @return {string} The URL to the FeatureServer/MapServer/GPServer.
*/
- createFeatureServerUrl: function(serviceUrl, serverName, format) {
+ createServerUrl: function(serviceUrl, serverName, format, serverType) {
if (!BasiGX.util.ArcGISRest.isArcGISRestUrl(serviceUrl)) {
return;
}
- var urlObj = new URL(serviceUrl);
+
+ // Working with the root Url ensures that this code works
+ // with both root and folder level services
+ var rootUrl = BasiGX.util.ArcGISRest.getArcGISRestRootUrl(
+ serviceUrl
+ );
+ var urlObj = new URL(rootUrl);
var parts = urlObj.pathname.split('/');
- parts.push(serverName);
- parts.push('FeatureServer');
- var path = parts.join('/');
+ var path = parts.concat([serverName, serverType]).join('/');
var url = urlObj.origin + path;
if (format) {
@@ -85,7 +90,40 @@ Ext.define('BasiGX.util.ArcGISRest', {
}
return url;
+ },
+
+ /**
+ * Creates the URL for a FeatureServer request.
+ *
+ * @param {string} serviceUrl The URL of the service.
+ * @param {string} serverName The name of the FeatureServer.
+ * @param {string} format The output format.
+ * @return {string} The URL to the FeatureServer.
+ */
+ createFeatureServerUrl: function(serviceUrl, serverName, format) {
+ return BasiGX.util.ArcGISRest.createServerUrl(
+ serviceUrl,
+ serverName,
+ format,
+ 'FeatureServer'
+ );
+ },
+ /**
+ * Creates the URL for a MapServer request.
+ *
+ * @param {string} serviceUrl The URL of the service.
+ * @param {string} serverName The name of the MapServer.
+ * @param {string} format The output format.
+ * @return {string} The URL to the MapServer.
+ */
+ createMapServerUrl: function(serviceUrl, serverName, format) {
+ return BasiGX.util.ArcGISRest.createServerUrl(
+ serviceUrl,
+ serverName,
+ format,
+ 'MapServer'
+ );
},
/**
@@ -139,8 +177,10 @@ Ext.define('BasiGX.util.ArcGISRest', {
* @param {number} layerConfig.layer.id The id of a FeatureServer layer.
* @param {string} layerConfig.layer.name The name of a FeatureServer
* layer.
- * @param {boolean} useDefaultHeader Whether to use the default
- * Xhr header.
+ * @param {Ext.data.TreeStore} layerConfig.subLayerStore The tree
+ * store containing the sublayers.
+ * @param {boolean} useDefaultHeader Whether to use the default Xhr
+ * header.
* @return {Ext.Promise} A promise containing the olLayer.
*/
createOlLayerFromArcGISRest: function(layerConfig, useDefaultHeader) {
@@ -152,6 +192,19 @@ Ext.define('BasiGX.util.ArcGISRest', {
Ext.log.warn('Provided URL is not a valid ArcGISRest URL');
return Ext.Promise.reject();
}
+
+ // collect all sublayer indexes that the user has marked as visible
+ var visibleLayerIndexes = [];
+ layerConfig.subLayerStore.each(function(sublayer){
+ var visibility = sublayer.get('visibility');
+ var layerId = sublayer.get('layerId');
+ var layerIdValid = Ext.isNumeric(layerId) && layerId >= 0;
+
+ if (visibility && layerIdValid){
+ visibleLayerIndexes.push(layerId);
+ }
+ });
+
var serviceUrl = [rootUrl, service.name, service.type].join('/');
var onReject = function() {
return Ext.Promise.reject();
@@ -172,7 +225,12 @@ Ext.define('BasiGX.util.ArcGISRest', {
source = new ol.source.TileArcGISRest({
url: serviceUrl,
projection: 'EPSG:' +
- serviceInfo.spatialReference.wkid
+ serviceInfo.spatialReference.wkid,
+ params: {
+ 'LAYERS': 'show:' +
+ visibleLayerIndexes.join(',')
+ }
+
});
layer = new ol.layer.Tile({
source: source,
diff --git a/src/view/form/AddArcGISRest.js b/src/view/form/AddArcGISRest.js
index e45c94d4e..f2fde697c 100644
--- a/src/view/form/AddArcGISRest.js
+++ b/src/view/form/AddArcGISRest.js
@@ -29,11 +29,16 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
'Ext.form.FieldSet',
'Ext.form.field.ComboBox',
'Ext.form.CheckboxGroup',
+ 'Ext.tree.Panel',
'Ext.Promise',
+ 'Ext.data.TreeStore',
+ 'Ext.data.Model',
+ 'Ext.data.proxy.Ajax',
'BasiGX.util.Map',
'BasiGX.util.MsgBox',
'BasiGX.util.Url',
- 'BasiGX.util.ArcGISRest'
+ 'BasiGX.util.ArcGISRest',
+ 'BasiGX.view.tree.ArcGISRestServiceTree'
],
viewModel: {
@@ -67,7 +72,9 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
msgInvalidUrl: 'Die angegebene URL ist keine valide ArcGISRest URL',
documentation: '
ArcGISRest Layer hinzufügen
• In ' +
'diesem Dialog können Sie mit Hilfe einer ArcGISRest-URL ' +
- 'einen beliebigen Kartendienst der Karte hinzufügen.'
+ 'einen beliebigen Kartendienst der Karte hinzufügen.',
+ serviceLayersVisibility: {},
+ availableLayersFieldSetMaxHeight: null
}
},
@@ -105,7 +112,7 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
/**
* Default url for the textfield or combobox.
*/
- defaultUrl: 'https://gis.epa.ie/arcgis/rest/services',
+ defaultUrl: 'https://gis.epa.ie/arcgis/rest/services/Copernicus',
/**
* Whether we will send the `X-Requested-With` header when fetching the
@@ -115,7 +122,12 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
*
* @type {Boolean}
*/
- useDefaultXhrHeader: false
+ useDefaultXhrHeader: false,
+
+ /**
+ * Allow parent configure available layers fieldset maxHeight
+ */
+ availableLayersFieldSetMaxHeight: null
},
/**
@@ -127,18 +139,18 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
defaultButton: 'requestLayersBtn',
+ layout: 'vbox',
+
items: [
{
xtype: 'fieldset',
- layout: 'anchor',
- defaults: {
- anchor: '100%'
- },
+ width: '100%',
bind: {
title: '{queryParamsFieldSetTitle}'
},
items: [{
xtype: 'textfield',
+ width: '100%',
bind: {
fieldLabel: '{arcGISUrlTextFieldLabel}'
},
@@ -183,19 +195,63 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
}
}
}
+ }, {
+ xtype: 'container',
+ layout: {
+ type: 'hbox',
+ pack: 'end'
+ },
+ items: [{
+ xtype: 'button',
+ name: 'resetFormBtn',
+ bind: {
+ text: '{resetBtnText}'
+ },
+ handler: function(btn) {
+ var view = btn.up('basigx-form-addarcgisrest');
+ view.getForm().reset();
+ view.removeAddLayersComponents();
+ view.resetState();
+ var defaultValue = view.defaultUrl;
+ var combo = view.down('combobox[name=urlCombo]');
+ combo.setValue(defaultValue);
+ var textfield = view.down('textfield[name=url]');
+ textfield.setValue(defaultValue);
+ var fs = view.down('[name=fs-available-layers]');
+ fs.setHidden(true);
+ }
+ }, {
+ xtype: 'button',
+ bind: {
+ text: '{requestLayersBtnText}'
+ },
+ margin: '0 0 0 5',
+ name: 'requestLayersBtn',
+ reference: 'requestLayersBtn',
+ formBind: true, // only enabled once the form is valid
+ disabled: true,
+ handler: function(btn) {
+ var view = btn.up('basigx-form-addarcgisrest');
+ view.resetState();
+ view.requestLayers()
+ .then(
+ view.onGetServicesSuccess.bind(view),
+ view.onGetServicesFailure.bind(view)
+ );
+ }
+ }]
}]
},
{
xtype: 'fieldset',
name: 'fs-available-layers',
- layout: 'anchor',
+ flex: 1,
+ width: '100%',
scrollable: 'y',
- maxHeight: 200,
- defaults: {
- anchor: '100%'
- },
+ hidden: true,
bind: {
- title: '{availableLayersFieldSetTitle}'
+ title: '{availableLayersFieldSetTitle}',
+ maxHeight: '{availableLayersFieldSetMaxHeight}'
},
items: {
xtype: 'checkboxgroup',
@@ -214,46 +270,6 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
}
],
- // Reset and Submit buttons
- buttons: [
- {
- name: 'resetFormBtn',
- bind: {
- text: '{resetBtnText}'
- },
- handler: function(btn) {
- var view = btn.up('basigx-form-addarcgisrest');
- view.getForm().reset();
- view.removeAddLayersComponents();
- view.resetState();
- var defaultValue = view.defaultUrl;
- var combo = view.down('combobox[name=urlCombo]');
- combo.setValue(defaultValue);
- var textfield = view.down('textfield[name=url]');
- textfield.setValue(defaultValue);
- }
- },
- '->',
- {
- bind: {
- text: '{requestLayersBtnText}'
- },
- name: 'requestLayersBtn',
- reference: 'requestLayersBtn',
- formBind: true, // only enabled once the form is valid
- disabled: true,
- handler: function(btn) {
- var view = btn.up('basigx-form-addarcgisrest');
- view.resetState();
- view.requestLayers()
- .then(
- view.onGetServicesSuccess.bind(view),
- view.onGetServicesFailure.bind(view)
- );
- }
- }
- ],
-
/**
* Initializes the form and sets up the parser instance.
*/
@@ -360,19 +376,19 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
return layerConfig.service.type === 'FeatureServer';
}
);
+ var nonFeatureServers = Ext.Array.filter(
+ layerConfigs, function(layerConfig) {
+ return layerConfig.service.type !== 'FeatureServer';
+ }
+ );
this.loadLayersOfFeatureServers(featureServers)
.then(function(featureServerConfigs) {
- layerConfigs = Ext.Array.filter(
- layerConfigs, function(layerConfig) {
- return layerConfig.service.type !== 'FeatureServer';
- }
- );
- layerConfigs = Ext.Array.merge(
- layerConfigs, featureServerConfigs);
- this.fillAvailableLayersFieldset(layerConfigs);
+ var mergedConfigs = Ext.Array.merge(
+ nonFeatureServers, featureServerConfigs);
+ this.fillAvailableLayersFieldset(mergedConfigs);
this.updateControlToolbarState();
this.setLoading(false);
- }.bind(this));
+ }.bind(this));
},
/**
@@ -407,7 +423,7 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
*/
loadLayersOfFeatureServers: function(featureServers) {
var me = this;
- var mappedPromises = Ext.Array.map(featureServers, function(server) {
+ var mappedPromises = Ext.Array.map(featureServers, function(server) {
return me.requestFeatureServer.call(me, server)
.then(function(res) {
var config = me.getFeatureServerConfigs(
@@ -608,16 +624,27 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
var cbGroup = fs.down('checkboxgroup');
var checkBoxes = [];
var candidatesInitiallyChecked = me.getCandidatesInitiallyChecked();
+
+ fs.setHidden(false);
+
Ext.each(layers, function(layer) {
- var boxLabel = layer.service.name;
- if (layer.service.type === 'FeatureServer') {
- boxLabel += '/' + layer.layer.name;
- }
checkBoxes.push({
- xtype: 'checkbox',
- boxLabel: boxLabel,
+ xtype: 'basigx-tree-arcgisrestservicetree',
+ arcGISLayerConfig: layer,
checked: candidatesInitiallyChecked,
- arcGISLayerConfig: layer
+ listeners: {
+ arcgisrestservicetreenodeexpand: function (expandedNode) {
+ me.requestLayer(layer).then(
+ function (response) {
+ me.onRequestLayerSuccess(
+ response,
+ expandedNode
+ );
+ },
+ me.onGetServicesFailure.bind(me)
+ );
+ }
+ }
});
});
cbGroup.add(checkBoxes);
@@ -662,6 +689,7 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
me.add({
xtype: 'toolbar',
name: 'interact-w-available-layers',
+ width: '100%',
items: tbItems
});
},
@@ -687,11 +715,37 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
addCheckedLayers: function() {
var me = this;
var fs = me.down('[name=fs-available-layers]');
- var checkboxes = fs.query('checkbox[checked=true][disabled=false]');
+
+ // collect checked layers from form
+ var layerItems = [];
+ var layerConfigTrees = fs.query('basigx-tree-arcgisrestservicetree');
+ Ext.each(layerConfigTrees, function(layerConfig){
+ var store = layerConfig.getStore();
+ if (!store) {
+ return;
+ }
+
+ var root = store.getRoot();
+ if (!root) {
+ return;
+ }
+
+ if (root.get('checked')) {
+ layerItems.push(layerConfig);
+ }
+ });
+
var map = BasiGX.util.Map.getMapComponent().getMap();
var useDefaultHeader = me.getUseDefaultXhrHeader();
- Ext.each(checkboxes, function(checkbox) {
- var config = checkbox.arcGISLayerConfig;
+ Ext.each(layerItems, function(layerItem) {
+ var config = layerItem.arcGISLayerConfig;
+
+ var subLayerStore = layerItem.getStore();
+ if (!subLayerStore){
+ return;
+ }
+
+ config.subLayerStore = subLayerStore;
BasiGX.util.ArcGISRest.createOlLayerFromArcGISRest(
config, useDefaultHeader
)
@@ -702,7 +756,7 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
me.fireEvent('beforearcgisrestadd', layer);
map.addLayer(layer);
me.fireEvent('arcgisrestadd', layer);
- checkbox.setDisabled(true);
+ layerItem.setDisabled(true);
me.updateControlToolbarState();
});
});
@@ -712,10 +766,11 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
* Checks all checkboxes in the available layers fieldset.
*/
checkAllLayers: function() {
- var sel = '[name=fs-available-layers] checkbox[disabled=false]';
- var checkboxes = this.query(sel);
- Ext.each(checkboxes, function(checkbox) {
- checkbox.setValue(true);
+ var sel = '[name=fs-available-layers]' +
+ ' basigx-tree-arcgisrestservicetree';
+ var trees = this.query(sel);
+ Ext.each(trees, function(tree) {
+ tree.getStore().getAt(0).set('checked', true);
});
},
@@ -723,10 +778,71 @@ Ext.define('BasiGX.view.form.AddArcGISRest', {
* Unchecks all checkboxes in the available layers fieldset.
*/
uncheckAllLayers: function() {
- var sel = '[name=fs-available-layers] checkbox[disabled=false]';
- var checkboxes = this.query(sel);
- Ext.each(checkboxes, function(checkbox) {
- checkbox.setValue(false);
+ var sel = '[name=fs-available-layers]' +
+ ' basigx-tree-arcgisrestservicetree';
+ var trees = this.query(sel);
+ Ext.each(trees, function(tree) {
+ tree.getStore().getAt(0).set('checked', false);
+ });
+ },
+
+ /**
+ * Set the viewModel property availableLayersFieldSetMaxHeight
+ * when the component config property availableLayersFieldSetMaxHeight
+ * changes to it can be used in a binding
+ *
+ * @param {Number} newValue Value to set
+ */
+ updateAvailableLayersFieldSetMaxHeight: function(newValue) {
+ var me = this;
+ var vm = me.getViewModel();
+ vm.set('availableLayersFieldSetMaxHeight', newValue);
+ },
+
+ /**
+ * Request layer to get information about sub layers within the layer
+ *
+ * @param {Object} config Layer ArcGIS layer config
+ */
+ requestLayer: function(config) {
+ var me = this;
+ var serviceUrl = BasiGX.util.ArcGISRest.createMapServerUrl(
+ config.url,
+ config.service.name,
+ 'json'
+ );
+ return new Ext.Promise(function (resolve, reject) {
+ Ext.Ajax.request({
+ url: serviceUrl,
+ method: 'GET',
+ useDefaultXhrHeader: me.getUseDefaultXhrHeader(),
+ success: function (response) {
+ var respJson = Ext.decode(response.responseText);
+ resolve(respJson);
+ },
+ failure: function (response) {
+ reject(response.status);
+ }
+ });
+ });
+ },
+
+ /**
+ * Request layer to get information about sub layers within the layer
+ *
+ * @param {Object} response ArcGIS Rest response
+ * @param {GeoExt.data.model.ArcGISRestServiceLayer} expandedNode Layer ArcGIS layer config
+ */
+ onRequestLayerSuccess: function(response, expandedNode) {
+ var layers = Ext.Array.map(response.layers, function(layer) {
+ return Ext.create('GeoExt.data.model.ArcGISRestServiceLayer',{
+ layerId: layer.id,
+ name: layer.name,
+ defaultVisibility: layer.defaultVisibility,
+ visibility: layer.defaultVisibility,
+ leaf: true
+ });
});
+ expandedNode.appendChild(layers);
}
});
diff --git a/src/view/tree/ArcGISRestServiceTree.js b/src/view/tree/ArcGISRestServiceTree.js
new file mode 100644
index 000000000..a3cf02b45
--- /dev/null
+++ b/src/view/tree/ArcGISRestServiceTree.js
@@ -0,0 +1,117 @@
+/* Copyright (c) 2022-present terrestris GmbH & Co. KG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+/**
+ * Used to add an ArcGIS REST layer to the map
+ *
+ * @class BasiGX.view.tree.ArcGISRestServiceTree
+ */
+Ext.define('BasiGX.view.tree.ArcGISRestServiceTree', {
+ extend: 'Ext.tree.Panel',
+ xtype: 'basigx-tree-arcgisrestservicetree',
+
+ requires: [
+ 'GeoExt.data.model.ArcGISRestServiceLayer',
+ 'BasiGX.util.ArcGISRest',
+ 'Ext.tree.Column'
+ ],
+
+ config: {
+ /**
+ * Whether we will send the `X-Requested-With` header when fetching the
+ * document from the URL. The `X-Requested-With` header is
+ * usually added for XHR, but adding it should lead to a preflight
+ * request (see https://goo.gl/6JzdUI), which some servers fail.
+ *
+ * @type {Boolean}
+ */
+ useDefaultXhrHeader: false
+ },
+
+ arcGISLayerConfig: null,
+
+
+ listeners: {
+ itemclick: function(view, record){
+ // toggle visibility of sublayer
+ var currentVisibility = record.get('visibility');
+ record.set('visibility', !currentVisibility);
+ },
+ beforecheckchange: function(node, checked){
+ // when layer is not checked anymore it will be collapsed
+ if (checked) {
+ node.collapse();
+ }
+ }
+ },
+
+ columns: {
+ header: false,
+ items: [{
+ xtype: 'treecolumn',
+ renderer: function(v, metaData, record) {
+ if (!record.isRoot()) {
+ var eyeGlyph = 'xf06e@FontAwesome';
+ var eyeSlashGlyph = 'xf070@FontAwesome';
+ if (record.get('visibility')) {
+ metaData.glyph = eyeGlyph;
+ } else {
+ metaData.glyph = eyeSlashGlyph;
+ }
+ }
+ return record.get('name');
+ }
+ }],
+ defaults: {
+ flex: 1
+ }
+ },
+
+ initComponent: function() {
+ var me = this;
+ me.callParent();
+
+ var rootLabel = me.arcGISLayerConfig.service.name;
+ if (me.arcGISLayerConfig.service.type === 'FeatureServer') {
+ rootLabel += '/' + me.arcGISLayerConfig.layer.name;
+ }
+
+ me.setStore({
+ model: 'GeoExt.data.model.ArcGISRestServiceLayer',
+ root: {
+ name: rootLabel,
+ checked: true,
+ children: []
+ },
+ listeners: {
+ 'nodeexpand': me.onNodeExpand.bind(me)
+ }
+ });
+ },
+
+ onNodeExpand: function(expandedNode) {
+ var me = this;
+
+ // ensure expanded layer is always checked
+ expandedNode.set('checked', true);
+
+ if (expandedNode.hasChildNodes()) {
+ return;
+ }
+
+ me.fireEvent('arcgisrestservicetreenodeexpand', expandedNode);
+ }
+
+});
diff --git a/test/resources/arcgis/copernicus-hotspotmonitoring-mapServer.json b/test/resources/arcgis/copernicus-hotspotmonitoring-mapServer.json
new file mode 100644
index 000000000..3db821f34
--- /dev/null
+++ b/test/resources/arcgis/copernicus-hotspotmonitoring-mapServer.json
@@ -0,0 +1,197 @@
+{
+ "currentVersion": 10.41,
+ "serviceDescription": "",
+ "mapName": "Layers",
+ "description": "",
+ "copyrightText": "",
+ "supportsDynamicLayers": false,
+ "layers": [
+ {
+ "id": 0,
+ "name": "Urban Atlas",
+ "parentLayerId": -1,
+ "defaultVisibility": false,
+ "subLayerIds": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8
+ ],
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 1,
+ "name": "Street Trees - Dublin",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 2,
+ "name": "Street Trees - Galway",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 3,
+ "name": "Street Trees - Waterford",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 4,
+ "name": "Land Cover - Cork",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 5,
+ "name": "Land Cover - Dublin",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 6,
+ "name": "Land Cover - Galway",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 7,
+ "name": "Land Cover - Limerick",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 8,
+ "name": "Land Cover - Waterford",
+ "parentLayerId": 0,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 9,
+ "name": "Riparian Zones",
+ "parentLayerId": -1,
+ "defaultVisibility": false,
+ "subLayerIds": [
+ 10,
+ 11
+ ],
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 10,
+ "name": "Green Linear Elements",
+ "parentLayerId": 9,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 11,
+ "name": "Land Cover",
+ "parentLayerId": 9,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 12,
+ "name": "Natura 2000",
+ "parentLayerId": -1,
+ "defaultVisibility": false,
+ "subLayerIds": [
+ 13
+ ],
+ "minScale": 0,
+ "maxScale": 0
+ },
+ {
+ "id": 13,
+ "name": "Natura 2000",
+ "parentLayerId": 12,
+ "defaultVisibility": false,
+ "subLayerIds": null,
+ "minScale": 0,
+ "maxScale": 0
+ }
+ ],
+ "tables": [],
+ "spatialReference": {
+ "wkid": 2157,
+ "latestWkid": 2157
+ },
+ "singleFusedMapCache": false,
+ "initialExtent": {
+ "xmin": -2192431.987076646,
+ "ymin": -1622206.551353204,
+ "xmax": 5343982.999664094,
+ "ymax": 3891074.1323311795,
+ "spatialReference": {
+ "wkid": 2157,
+ "latestWkid": 2157
+ }
+ },
+ "fullExtent": {
+ "xmin": 448851.36479999963,
+ "ymin": 533041.3618999999,
+ "xmax": 754159.6705,
+ "ymax": 950054.5759999994,
+ "spatialReference": {
+ "wkid": 2157,
+ "latestWkid": 2157
+ }
+ },
+ "minScale": 0,
+ "maxScale": 0,
+ "units": "esriMeters",
+ "supportedImageFormatTypes": "PNG32,PNG24,PNG,JPG,DIB,TIFF,EMF,PS,PDF,GIF,SVG,SVGZ,BMP",
+ "documentInfo": {
+ "Title": "",
+ "Author": "",
+ "Comments": "",
+ "Subject": "The European Environment Agency, as lead authority under the Copernicus Land Monitoring Service, co-ordinate the development of high resolution \u201clocal component\u201d or \u201chot spot monitoring\u201d datasets. Comparable data is produced across Europe using earth",
+ "Category": "",
+ "AntialiasingMode": "None",
+ "TextAntialiasingMode": "Force",
+ "Keywords": "Copernicus,Land Cover,Land Use,Riparian Zones,Natura,Urban Atlas,Hot Spot Monitoring"
+ },
+ "capabilities": "Map,Query,Data",
+ "supportedQueryFormats": "JSON, AMF, geoJSON",
+ "exportTilesAllowed": false,
+ "maxRecordCount": 1000,
+ "maxImageHeight": 4096,
+ "maxImageWidth": 4096,
+ "supportedExtensions": "KmlServer"
+}
diff --git a/test/resources/arcgis/copernicus.json b/test/resources/arcgis/copernicus.json
new file mode 100644
index 000000000..e80cf988a
--- /dev/null
+++ b/test/resources/arcgis/copernicus.json
@@ -0,0 +1,14 @@
+{
+ "currentVersion": 10.41,
+ "folders": [],
+ "services": [
+ {
+ "name": "Copernicus/HighResolutionLayers",
+ "type": "MapServer"
+ },
+ {
+ "name": "Copernicus/HotSpotMonitoring",
+ "type": "MapServer"
+ }
+ ]
+}
diff --git a/test/resources/arcgis/services.json b/test/resources/arcgis/services.json
new file mode 100644
index 000000000..68a533b8e
--- /dev/null
+++ b/test/resources/arcgis/services.json
@@ -0,0 +1,16 @@
+{
+ "currentVersion": 10.41,
+ "folders": [
+ "BW",
+ "Copernicus",
+ "EPAMapServices",
+ "TimpeallAnTi",
+ "Utilities"
+ ],
+ "services": [
+ {
+ "name": "BasemapHW",
+ "type": "MapServer"
+ }
+ ]
+}
diff --git a/test/spec/util/ArcGISRest.test.js b/test/spec/util/ArcGISRest.test.js
index 9b062f02f..b834168ff 100644
--- a/test/spec/util/ArcGISRest.test.js
+++ b/test/spec/util/ArcGISRest.test.js
@@ -100,4 +100,35 @@ describe('BasiGX.util.ArcGISRest', function() {
});
});
+
+ describe('#createMapServerUrl', function() {
+
+ it('returns the MapServer url when the service in on the root', function() {
+ var serviceUrl = 'http://example.com/services';
+ var mapServerName = 'foo';
+ var url = serviceUrl + '/' + mapServerName + '/MapServer';
+ var result = BasiGX.util.ArcGISRest.createMapServerUrl(
+ serviceUrl, mapServerName);
+ expect(result).to.equal(url);
+ });
+
+ it('returns the MapServer url when the service is in a folder', function() {
+ var serviceUrl = 'http://example.com/services/folder';
+ var mapServerName = 'folder/servername';
+ var expectedUrl = 'http://example.com/services/folder/servername/MapServer';
+ var result = BasiGX.util.ArcGISRest.createMapServerUrl(
+ serviceUrl, mapServerName);
+ expect(result).to.equal(expectedUrl);
+ });
+
+ it('returns the MapServer url with a specified format', function() {
+ var serviceUrl = 'http://example.com/services';
+ var featureServerName = 'foo';
+ var format = 'bar';
+ var url = serviceUrl + '/' + featureServerName + '/MapServer?f=' + format;
+ var result = BasiGX.util.ArcGISRest.createMapServerUrl(
+ serviceUrl, featureServerName, format);
+ expect(result).to.equal(url);
+ });
+ });
});
diff --git a/test/spec/view/form/AddArcGISRest.test.js b/test/spec/view/form/AddArcGISRest.test.js
index 76634edef..8ef4eff83 100644
--- a/test/spec/view/form/AddArcGISRest.test.js
+++ b/test/spec/view/form/AddArcGISRest.test.js
@@ -94,6 +94,19 @@ describe('BasiGX.view.form.AddArcGISRest', function() {
describe('Methods', function() {
var form = null;
+ var layers = [{
+ service: {
+ name: 'Copernicus/HighResolutionLayers',
+ type: 'MapServer'
+ },
+ url: 'https://gis.epa.ie/arcgis/rest/services/Copernicus?f=json'
+ },{
+ service: {
+ name: 'Copernicus/HotSpotMonitoring',
+ type: 'MapServer'
+ },
+ url: 'https://gis.epa.ie/arcgis/rest/services/Copernicus?f=json'
+ }];
beforeEach(function() {
form = Ext.create('BasiGX.view.form.AddArcGISRest', {
renderTo: Ext.getBody()
@@ -185,5 +198,173 @@ describe('BasiGX.view.form.AddArcGISRest', function() {
});
});
});
+ describe('checkAllLayers / uncheckAllLayers', function() {
+ it('checks and unchecks all layers', function() {
+ function isAllChecked(trees) {
+ return Ext.Array.every(trees, function (tree) {
+ return tree.getStore().getAt(0).get('checked');
+ });
+ }
+
+ form.fillAvailableLayersFieldset(layers);
+ var trees = form.query('[name=fs-available-layers] basigx-tree-arcgisrestservicetree');
+ expect(isAllChecked(trees)).to.be(true);
+
+ form.uncheckAllLayers();
+ expect(isAllChecked(trees)).to.be(false);
+
+ form.checkAllLayers();
+ expect(isAllChecked(trees)).to.be(true);
+ });
+ });
+ });
+
+ describe('Behaviour', function() {
+ var form = null;
+ var xhr;
+ var requests;
+ beforeEach(function() {
+ xhr = sinon.useFakeXMLHttpRequest();
+ requests = [];
+ xhr.onCreate = function(xhr) {
+ requests.push(xhr);
+ };
+ form = Ext.create('BasiGX.view.form.AddArcGISRest', {
+ renderTo: Ext.getBody()
+ });
+ });
+ afterEach(function() {
+ xhr.restore();
+ //requests = [];
+ if (form) {
+ form.destroy();
+ form = null;
+ }
+ });
+
+ function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+ }
+
+ async function fakeXhrResponse(path) {
+ const res = await fetch(path);
+ const json = await res.text();
+ requests[requests.length - 1].respond(200, { 'Content-Type': 'application/json' }, json);
+ // allow time for Extjs to render UI items
+ await sleep(10);
+ }
+
+ it('fetches root services and creates checkboxgroups', async function() {
+ var spy = sinon.spy(form, 'requestLayers');
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ await fakeXhrResponse('/resources/arcgis/services.json');
+
+ var checkboxes = form.query('basigx-tree-arcgisrestservicetree');
+ expect(checkboxes.length).to.be(1);
+ expect(spy.called).to.be(true);
+ });
+
+ it('fetches services and creates checkboxgroups', async function() {
+ var spy = sinon.spy(form, 'requestLayers');
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ await fakeXhrResponse('/resources/arcgis/copernicus.json');
+
+ var checkboxes = form.query('basigx-tree-arcgisrestservicetree');
+ expect(checkboxes.length).to.be(2);
+ expect(spy.called).to.be(true);
+ });
+
+ it('fetches layers and populates checkboxgroups when a checkboxgroup is expanded', async function() {
+ var spy = sinon.spy(form, 'requestLayer');
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ await fakeXhrResponse('/resources/arcgis/copernicus.json');
+
+ var tree = form.down('basigx-tree-arcgisrestservicetree');
+ tree.expandNode(tree.getStore().getAt(0));
+ await fakeXhrResponse('/resources/arcgis/copernicus-hotspotmonitoring-mapServer.json');
+
+ expect(tree.getStore().getAt(0).childNodes.length).to.be(14);
+ expect(spy.called).to.be(true);
+ });
+
+ it('adds checked layers', async function() {
+ var spy = sinon.spy();
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ await fakeXhrResponse('/resources/arcgis/copernicus.json');
+
+ var tree = form.down('basigx-tree-arcgisrestservicetree');
+ tree.expandNode(tree.getStore().getAt(0));
+ await fakeXhrResponse('/resources/arcgis/copernicus-hotspotmonitoring-mapServer.json');
+
+ form.down('[name=add-checked-layers]').click();
+
+ // monitor arcgisrestadd events
+ form.on('arcgisrestadd', spy);
+
+ // respond again with layer info since createOlLayerFromArcGISRest calls the service url again
+ // two layers are checked so two xhr requests are created in quick succession
+ // requests are created
+ const res = await fetch('/resources/arcgis/copernicus-hotspotmonitoring-mapServer.json');
+ const json = await res.text();
+ requests[requests.length - 2].respond(200, { 'Content-Type': 'application/json' }, json);
+ requests[requests.length - 1].respond(200, { 'Content-Type': 'application/json' }, json);
+ await sleep(10);
+
+ // two layers added, means two arcgisrestadd events fired
+ expect(spy.callCount).to.be(2);
+ });
+
+ it('handles service call errors', async function() {
+ var spy = sinon.spy(form, 'onGetServicesFailure');
+ var codes = [0, 400, 401, 403, 404, 429, 500, 503, 504, 505];
+ var count = 0;
+ for (var code of codes) {
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ requests[requests.length - 1].respond(code);
+ count++;
+ await sleep(10);
+ expect(spy.callCount).to.be(count);
+ }
+ });
+
+ it('handles arcgis rest errors', async function() {
+ // doesn't seem to be an easy way to check if the warn dialog is present
+ // instead check the getErrorMessage is called along with onGetServicesSuccess
+ // this means the response is 200 but body has an arcgis rest error
+ var spy1 = sinon.spy(form, 'onGetServicesSuccess');
+ var spy2 = sinon.spy(form, 'getErrorMessage');
+
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ requests[requests.length - 1].respond(
+ 200,
+ { 'Content-Type': 'application/json' },
+ '{"error":{"code":404,"message":"Folder not found","details":[]}}');
+ await sleep(10);
+
+ expect(spy1.callCount).to.be(1);
+ expect(spy2.callCount).to.be(1);
+ });
+
+ it('resets ui and state on reset button click', async function() {
+ // populate the ui
+ form.down('[name="url"]').setValue('https://test.com/services/service');
+ form.down('[name="requestLayersBtn"]').click();
+ await fakeXhrResponse('/resources/arcgis/services.json');
+ var fs = form.down('[name=fs-available-layers]');
+
+ // verify available-layers is show, then click reset
+ expect(fs.hidden).to.be(false);
+ form.down('[name="resetFormBtn"]').click();
+
+ // verify available-layers is hidden and url textbox has been reset
+ expect(form.down('[name="url"]').getValue()).to.not.be('https://test.com/services/service');
+ expect(fs.hidden).to.be(true);
+ });
});
});