diff --git a/web/client/actions/__tests__/catalog-test.js b/web/client/actions/__tests__/catalog-test.js
index 360225a39a..85fe92feae 100644
--- a/web/client/actions/__tests__/catalog-test.js
+++ b/web/client/actions/__tests__/catalog-test.js
@@ -7,7 +7,9 @@
*/
const expect = require('expect');
-const {getRecords, addLayerError, ADD_LAYER_ERROR} = require('../catalog');
+const LayersUtils = require('../../utils/LayersUtils');
+const {getRecords, addLayerError, addLayer, ADD_LAYER_ERROR} = require('../catalog');
+const {CHANGE_LAYER_PROPERTIES, ADD_LAYER} = require('../layers');
describe('Test correctness of the catalog actions', () => {
it('getRecords ISO Metadata Profile', (done) => {
@@ -55,7 +57,29 @@ describe('Test correctness of the catalog actions', () => {
}
});
});
-
+ it('add layer and describe it', (done) => {
+ const verify = (action) => {
+ if (action.type === ADD_LAYER) {
+ expect(action.layer).toExist();
+ const layer = action.layer;
+ expect(layer.id).toExist();
+ expect(layer.id).toBe(LayersUtils.getLayerId(action.layer, []));
+ } else if (action.type === CHANGE_LAYER_PROPERTIES) {
+ expect(action.layer).toExist();
+ expect(action.newProperties).toExist();
+ expect(action.newProperties.search).toExist();
+ expect(action.newProperties.search.type ).toBe('wfs');
+ expect(action.newProperties.search.url).toBe("http://some.geoserver.org:80/geoserver/wfs?");
+ done();
+ }
+ };
+ const callback = addLayer({
+ url: 'base/web/client/test-resources/wms/DescribeLayers.xml',
+ type: 'wms',
+ name: 'workspace:vector_layer'
+ });
+ callback(verify, () => ({ layers: []}));
+ });
it('sets an error on addLayerError action', () => {
const action = addLayerError('myerror');
diff --git a/web/client/actions/catalog.js b/web/client/actions/catalog.js
index ce7a49045d..52f45e7ff0 100644
--- a/web/client/actions/catalog.js
+++ b/web/client/actions/catalog.js
@@ -11,6 +11,11 @@ var API = {
wms: require('../api/WMS')
};
+const {addLayer, changeLayerProperties} = require('./layers');
+
+const LayersUtils = require('../utils/LayersUtils');
+const {find} = require('lodash');
+
const RECORD_LIST_LOADED = 'RECORD_LIST_LOADED';
const RECORD_LIST_LOAD_ERROR = 'RECORD_LIST_LOAD_ERROR';
const CHANGE_CATALOG_FORMAT = 'CHANGE_CATALOG_FORMAT';
@@ -75,7 +80,32 @@ function textSearch(format, url, startPosition, maxRecords, text, options) {
});
};
}
+function addLayerAndDescribe(layer) {
+ return (dispatch, getState) => {
+ const state = getState();
+ const layers = state && state.layers;
+ const id = LayersUtils.getLayerId(layer, layers || []);
+ dispatch(addLayer({...layer, id}));
+ if (layer.type === 'wms') {
+ // try to describe layer
+ return API.wms.describeLayers(layer.url, layer.name).then((results) => {
+ if (results) {
+ let description = find(results, (desc) => desc.name === layer.name );
+ if (description && description.owsType === 'WFS') {
+ dispatch(changeLayerProperties(id, {
+ search: {
+ url: description.owsURL,
+ type: 'wfs'
+ }
+ }));
+ }
+ }
+
+ });
+ }
+ };
+}
function addLayerError(error) {
return {
type: ADD_LAYER_ERROR,
@@ -98,6 +128,7 @@ module.exports = {
getRecords,
textSearch,
changeCatalogFormat,
+ addLayer: addLayerAndDescribe,
addLayerError,
catalogReset
};
diff --git a/web/client/api/WMS.js b/web/client/api/WMS.js
index 095fb91c93..b71a133c8f 100644
--- a/web/client/api/WMS.js
+++ b/web/client/api/WMS.js
@@ -111,6 +111,32 @@ const Api = {
return searchAndPaginate(json, startPosition, maxRecords, text);
});
},
+ describeLayers: function(url, layers) {
+ const parsed = urlUtil.parse(url, true);
+ const describeLayerUrl = urlUtil.format(assign({}, parsed, {
+ query: assign({
+ service: "WMS",
+ version: "1.1.1",
+ layers: layers,
+ request: "DescribeLayer"
+ }, parsed.query)
+ }));
+ return axios.get(parseUrl(describeLayerUrl)).then((response) => {
+ let decriptions;
+ xml2js.parseString(response.data, {explicitArray: false}, (ignore, result) => {
+ decriptions = result && result.WMS_DescribeLayerResponse && result.WMS_DescribeLayerResponse.LayerDescription;
+ });
+ decriptions = Array.isArray(decriptions) ? decriptions : [decriptions];
+ // make it compatible with json format of describe layer
+ return decriptions.map(desc => ({
+ ...(desc && desc.$ || {}),
+ layerName: desc.$ && desc.$.name,
+ query: {
+ ...(desc && desc.query && desc.query.$ || {})
+ }
+ }));
+ });
+ },
textSearch: function(url, startPosition, maxRecords, text) {
return Api.getRecords(url, startPosition, maxRecords, text);
}
diff --git a/web/client/api/__tests__/WMS-test.js b/web/client/api/__tests__/WMS-test.js
new file mode 100644
index 0000000000..eceed9330e
--- /dev/null
+++ b/web/client/api/__tests__/WMS-test.js
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2016, GeoSolutions Sas.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const expect = require('expect');
+const API = require('../WMS');
+
+describe('Test correctness of the WMS APIs', () => {
+ it('describeLayers', (done) => {
+ API.describeLayers('base/web/client/test-resources/wms/DescribeLayers.xml', "workspace:vector_layer").then((result) => {
+ try {
+ expect(result).toExist();
+ expect(result.length).toBe(2);
+ expect(result[0].owsType).toBe("WFS");
+ done();
+ } catch(ex) {
+ done(ex);
+ }
+ });
+ });
+ it('describeLayer with OGC-SCHEMAS', (done) => {
+ API.describeLayer('base/web/client/test-resources/wms/DescribeLayers.xml', "workspace:vector_layer").then((result) => {
+ try {
+ expect(result).toExist();
+ expect(result.owsType).toBe("WFS");
+ done();
+ } catch(ex) {
+ done(ex);
+ }
+ });
+ });
+ it('GetCapabilities 1.3.0', (done) => {
+ API.getCapabilities('base/web/client/test-resources/wms/GetCapabilities-1.3.0.xml').then((result) => {
+ try {
+ expect(result).toExist();
+ expect(result.capability).toExist();
+ expect(result.version).toBe("1.3.0");
+ expect(result.capability.layer).toExist();
+ done();
+ } catch(ex) {
+ done(ex);
+ }
+ });
+ });
+ it('GetCapabilities 1.1.1', (done) => {
+ API.getCapabilities('base/web/client/test-resources/wms/GetCapabilities-1.1.1.xml').then((result) => {
+ try {
+ expect(result).toExist();
+ expect(result.capability).toExist();
+ expect(result.version).toBe("1.1.1");
+ expect(result.capability.layer).toExist();
+ done();
+ } catch(ex) {
+ done(ex);
+ }
+ });
+ });
+ it('GetRecords', (done) => {
+ API.getRecords('base/web/client/test-resources/wms/GetCapabilities-1.3.0.xml', 0, 1, '').then((result) => {
+ try {
+ expect(result).toExist();
+ expect(result.numberOfRecordsMatched).toBe(5);
+ done();
+ } catch(ex) {
+ done(ex);
+ }
+ });
+ });
+});
diff --git a/web/client/plugins/MetadataExplorer.jsx b/web/client/plugins/MetadataExplorer.jsx
index 1e786725cc..92f3e11457 100644
--- a/web/client/plugins/MetadataExplorer.jsx
+++ b/web/client/plugins/MetadataExplorer.jsx
@@ -11,8 +11,7 @@ const {connect} = require('react-redux');
const assign = require('object-assign');
const {createSelector} = require("reselect");
const {Glyphicon, Panel} = require('react-bootstrap');
-const {textSearch, changeCatalogFormat, addLayerError, catalogReset} = require("../actions/catalog");
-const {addLayer} = require("../actions/layers");
+const {textSearch, changeCatalogFormat, addLayer, addLayerError, catalogReset} = require("../actions/catalog");
const {zoomToExtent} = require("../actions/map");
const {toggleControl} = require("../actions/controls");
const Message = require("../components/I18N/Message");
diff --git a/web/client/plugins/Save.jsx b/web/client/plugins/Save.jsx
index 93186e7e7c..467fc156a2 100644
--- a/web/client/plugins/Save.jsx
+++ b/web/client/plugins/Save.jsx
@@ -87,6 +87,7 @@ const Save = React.createClass({
features: layer.features,
format: layer.format,
group: layer.group,
+ search: layer.search,
source: layer.source,
name: layer.name,
opacity: layer.opacity,
diff --git a/web/client/plugins/SaveAs.jsx b/web/client/plugins/SaveAs.jsx
index fe84e42f58..e806900aaf 100644
--- a/web/client/plugins/SaveAs.jsx
+++ b/web/client/plugins/SaveAs.jsx
@@ -119,6 +119,7 @@ const SaveAs = React.createClass({
features: layer.features,
format: layer.format,
group: layer.group,
+ search: layer.search,
source: layer.source,
name: layer.name,
opacity: layer.opacity,
diff --git a/web/client/reducers/layers.js b/web/client/reducers/layers.js
index 01ea66f450..a3a2c9a0f5 100644
--- a/web/client/reducers/layers.js
+++ b/web/client/reducers/layers.js
@@ -246,7 +246,7 @@ function layers(state = [], action) {
case ADD_LAYER: {
let newLayers = (state.flat || []).concat();
let newGroups = (state.groups || []).concat();
- const newLayer = (action.layer.id) ? action.layer : assign({}, action.layer, {id: action.layer.name + "__" + newLayers.length});
+ const newLayer = (action.layer.id) ? action.layer : assign({}, action.layer, {id: LayersUtils.getLayerId(action.layer, newLayers)});
newLayers.push(newLayer);
const groupName = newLayer.group || 'Default';
if (groupName !== "background") {
diff --git a/web/client/reducers/query.js b/web/client/reducers/query.js
index 18ab40f77f..98a5770d71 100644
--- a/web/client/reducers/query.js
+++ b/web/client/reducers/query.js
@@ -26,7 +26,8 @@ const assign = require('object-assign');
const types = {
'xsd:string': 'string',
'xsd:dateTime': 'date',
- 'xsd:number': 'number'
+ 'xsd:number': 'number',
+ 'xsd:int': 'number'
};
const fieldConfig = {};
const extractInfo = (featureType) => {
diff --git a/web/client/test-resources/wms/DescribeLayers.xml b/web/client/test-resources/wms/DescribeLayers.xml
new file mode 100644
index 0000000000..e072d7a9f1
--- /dev/null
+++ b/web/client/test-resources/wms/DescribeLayers.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/web/client/test-resources/wms/GetCapabilities-1.1.1.xml b/web/client/test-resources/wms/GetCapabilities-1.1.1.xml
new file mode 100644
index 0000000000..2076e92218
--- /dev/null
+++ b/web/client/test-resources/wms/GetCapabilities-1.1.1.xml
@@ -0,0 +1,297 @@
+
+
+
+
+ OGC:WMS
+ GeoServer Web Map Service
+ A compliant implementation of WMS plus most of the SLD extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS
+
+ WFS
+ WMS
+ GEOSERVER
+
+
+
+
+ Claudius Ptolomaeus
+ The ancient geographes INC
+
+ Chief geographer
+
+ Work
+
+ Alexandria
+
+
+ Egypt
+
+
+
+ claudius.ptolomaeus@gmail.com
+
+ NONE
+ NONE
+
+
+
+
+ application/vnd.ogc.wms_xml
+
+
+
+
+
+
+
+
+
+
+
+
+ image/png
+ application/atom xml
+ application/atom+xml
+ application/json;type=utfgrid
+ application/openlayers
+ application/pdf
+ application/rss xml
+ application/rss+xml
+ application/vnd.google-earth.kml
+ application/vnd.google-earth.kml xml
+ application/vnd.google-earth.kml+xml
+ application/vnd.google-earth.kml+xml;mode=networklink
+ application/vnd.google-earth.kmz
+ application/vnd.google-earth.kmz xml
+ application/vnd.google-earth.kmz+xml
+ application/vnd.google-earth.kmz;mode=networklink
+ application/x-gpkg
+ application/x-sqlite3
+ atom
+ geopackage
+ geopkg
+ gpkg
+ image/geotiff
+ image/geotiff8
+ image/gif
+ image/gif;subtype=animated
+ image/jpeg
+ image/png8
+ image/png; mode=8bit
+ image/svg
+ image/svg xml
+ image/svg+xml
+ image/tiff
+ image/tiff8
+ image/vnd.jpeg-png
+ kml
+ kmz
+ mbtiles
+ openlayers
+ rss
+ text/html; subtype=openlayers
+ utfgrid
+
+
+
+
+
+
+
+
+
+ text/plain
+ application/vnd.ogc.gml
+ text/xml
+ application/vnd.ogc.gml/3.1.1
+ text/xml; subtype=gml/3.1.1
+ text/html
+ application/json
+
+
+
+
+
+
+
+
+
+
+
+
+ application/vnd.ogc.wms_xml
+
+
+
+
+
+
+
+
+
+ image/png
+ image/jpeg
+ image/gif
+
+
+
+
+
+
+
+
+
+ application/vnd.ogc.sld+xml
+
+
+
+
+
+
+
+
+
+
+ application/vnd.ogc.se_xml
+ application/vnd.ogc.se_inimage
+ application/vnd.ogc.se_blank
+ application/json
+
+
+
+ GeoServer Web Map Service
+ A compliant implementation of WMS plus most of the SLD extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS
+
+ EPSG:4326
+ EPSG:3857
+
+
+ ny-parent-group
+ Layer-Group type layer: ny-parent-group
+ EPSG:4326
+
+
+
+ spearfish
+ spearfish
+ Layer-Group type layer: spearfish
+ EPSG:26713
+
+
+
+
+ tiger-ny
+ tiger-ny
+ Layer-Group type layer: tiger-ny
+ EPSG:4326
+
+
+
+
+
+ tasmania
+ tasmania
+ Layer-Group type layer: tasmania
+ EPSG:4326
+
+
+
+
+ nurc:Arc_Sample
+ A sample ArcGrid file
+
+
+ WCS
+ arcGridSample
+ arcGridSample_Coverage
+
+ EPSG:4326
+
+
+
+
+
+
+ sde:GRAY_HR_SR_OB_DR
+ GRAY_HR_SR_OB_DR
+ Gray Earth with Shaded Relief, Hypsography, Ocean Bottom, and Drainages
+
+ WCS
+ GeoTIFF
+ GRAY_HR_SR_OB_DR
+
+ EPSG:4326
+
+
+
+
+
+ sde:HYP_HR_SR_OB_DR
+ HYP_HR_SR_OB_DR
+ Cross Blended Hypso with Relief, Water, Drains, and Ocean Bottom
+
+ WCS
+ GeoTIFF
+ HYP_HR_SR_OB_DR
+
+ EPSG:4326
+
+
+
+
+
+ nurc:Img_Sample
+ North America sample imagery
+
+
+ WCS
+ worldImageSample
+ worldImageSample_Coverage
+
+ EPSG:4326
+
+
+
+
+
+
+
+
diff --git a/web/client/test-resources/wms/GetCapabilities-1.3.0.xml b/web/client/test-resources/wms/GetCapabilities-1.3.0.xml
new file mode 100644
index 0000000000..7dde561bcd
--- /dev/null
+++ b/web/client/test-resources/wms/GetCapabilities-1.3.0.xml
@@ -0,0 +1,234 @@
+
+
+
+ WMS
+ GeoServer Web Map Service
+ A compliant implementation of WMS plus most of the SLD extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS
+
+ WFS
+ WMS
+ GEOSERVER
+
+
+
+
+ Claudius Ptolomaeus
+ The ancient geographes INC
+
+ Chief geographer
+
+ Work
+
+ Alexandria
+
+
+ Egypt
+
+
+
+ claudius.ptolomaeus@gmail.com
+
+ NONE
+ NONE
+
+
+
+
+ text/xml
+
+
+
+
+
+
+
+
+
+
+
+
+ image/png
+ application/atom+xml
+ application/json;type=utfgrid
+ application/pdf
+ application/rss+xml
+ application/vnd.google-earth.kml+xml
+ application/vnd.google-earth.kml+xml;mode=networklink
+ application/vnd.google-earth.kmz
+ application/x-gpkg
+ application/x-sqlite3
+ image/geotiff
+ image/geotiff8
+ image/gif
+ image/jpeg
+ image/png; mode=8bit
+ image/svg+xml
+ image/tiff
+ image/tiff8
+ image/vnd.jpeg-png
+ text/html; subtype=openlayers
+
+
+
+
+
+
+
+
+
+ text/plain
+ application/vnd.ogc.gml
+ text/xml
+ application/vnd.ogc.gml/3.1.1
+ text/xml; subtype=gml/3.1.1
+ text/html
+ application/json
+
+
+
+
+
+
+
+
+
+
+ XML
+ INIMAGE
+ BLANK
+ JSON
+
+
+ GeoServer Web Map Service
+ A compliant implementation of WMS plus most of the SLD extension (dynamic styling). Can also generate PDF, SVG, KML, GeoRSS
+
+ EPSG:3857
+ EPSG:4326
+ CRS:84
+
+ -180.0
+ 180.0
+ -90.0
+ 90.0
+
+
+
+ ny-parent-group
+ Layer-Group type layer: ny-parent-group
+ EPSG:4326
+
+ -180.0
+ 180.0
+ -90.0
+ 90.0
+
+
+
+ spearfish
+ spearfish
+ Layer-Group type layer: spearfish
+ EPSG:26713
+
+ -103.87791475407893
+ -103.62278893469492
+ 44.37246687108142
+ 44.50235105543566
+
+
+
+
+ tiger-ny
+ tiger-ny
+ Layer-Group type layer: tiger-ny
+ EPSG:4326
+
+ -74.047185
+ -73.907005
+ 40.679648
+ 40.882078
+
+
+
+
+
+ tasmania
+ tasmania
+ Layer-Group type layer: tasmania
+ EPSG:4326
+
+ 143.83482400000003
+ 148.47914100000003
+ -43.648056
+ -39.573891
+
+
+
+
+ nurc:Arc_Sample
+ A sample ArcGrid file
+
+
+ WCS
+ arcGridSample
+ arcGridSample_Coverage
+
+ EPSG:4326
+ CRS:84
+
+ -180.0
+ 180.0
+ -90.0
+ 90.0
+
+
+
+
+
+
+
+ sde:GRAY_HR_SR_OB_DR
+ GRAY_HR_SR_OB_DR
+ Gray Earth with Shaded Relief, Hypsography, Ocean Bottom, and Drainages
+
+ WCS
+ GeoTIFF
+ GRAY_HR_SR_OB_DR
+
+ EPSG:4326
+ CRS:84
+
+ -180.0
+ 180.00000000007202
+ -90.000000000036
+ 90.00000000000001
+
+
+
+
+
+
+
+
diff --git a/web/client/utils/LayersUtils.js b/web/client/utils/LayersUtils.js
index b86ae53061..be655e5ca6 100644
--- a/web/client/utils/LayersUtils.js
+++ b/web/client/utils/LayersUtils.js
@@ -25,6 +25,9 @@ const reorderLayers = (groups, allLayers) => {
};
var LayersUtils = {
+ getLayerId: (layerObj, layers) => {
+ return layerObj && layerObj.id || (layerObj.name + "__" + layers.length);
+ },
getLayersByGroup: (configLayers) => {
let i = 0;
let mapLayers = configLayers.map((layer) => assign({}, layer, {storeIndex: i++}));