Skip to content

Commit

Permalink
Close #593. Metadata Explorer first implementation (#585)
Browse files Browse the repository at this point in the history
  • Loading branch information
offtherailz committed May 20, 2016
1 parent 830cd58 commit 66d454e
Show file tree
Hide file tree
Showing 27 changed files with 1,776 additions and 2 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"intl": "1.2.2",
"ismobilejs": "0.4.0",
"json-loader": "0.5.4",
"jsonix": "2.4.1",
"keymirror": "0.1.1",
"leaflet": "0.7.7",
"leaflet-draw": "0.2.4",
Expand All @@ -87,6 +88,8 @@
"moment": "2.13.0",
"node-uuid": "1.4.3",
"object-assign": "3.0.0",
"ogc-schemas": "2.6.1",
"w3c-schemas": "1.3.1",
"openlayers": "3.15.1",
"pdfviewer": "0.3.2",
"proj4": "2.3.14",
Expand Down
47 changes: 47 additions & 0 deletions web/client/actions/__tests__/catalog-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* 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 {getRecords} = require('../catalog');
describe('Test correctness of the catalog actions', () => {

it('getRecords ISO Metadata Profile', (done) => {
getRecords('base/web/client/test-resources/csw/getRecordsResponseISO.xml', 1, 1)((actionResult) => {
try {
let result = actionResult && actionResult.result;
expect(result).toExist();
expect(result.records).toExist();
expect(result.records.length).toBe(1);
done();
} catch(ex) {
done(ex);
}
});
});
it('getRecords Dublin Core', (done) => {
getRecords('base/web/client/test-resources/csw/getRecordsResponseDC.xml', 1, 2)((actionResult) => {
try {
let result = actionResult && actionResult.result;
expect(result).toExist();
expect(result.records).toExist();
expect(result.records.length).toBe(2);
let rec0 = result.records[0];
expect(rec0.dc).toExist();
expect(rec0.dc.URI).toExist();
expect(rec0.dc.URI[0]);
let uri = rec0.dc.URI[0];
expect(uri.name).toExist();
expect(uri.value).toExist();
expect(uri.description).toExist();
done();
} catch(ex) {
done(ex);
}
});
});
});
59 changes: 59 additions & 0 deletions web/client/actions/catalog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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.
*/

var API = require('../api/CSW');

const RECORD_LIST_LOADED = 'RECORD_LIST_LOADED';
const RECORD_LIST_LOAD_ERROR = 'RECORD_LIST_LOAD_ERROR';

function recordsLoaded(options, result) {
return {
type: RECORD_LIST_LOADED,
searchOptions: options,
result: result
};
}

function recordsLoadError(e) {
return {
type: RECORD_LIST_LOAD_ERROR,
error: e
};
}
function getRecords(url, startPosition = 1, maxRecords, filter, options) {
return (dispatch /* , getState */) => {
// TODO auth (like) let opts = GeoStoreApi.getAuthOptionsFromState(getState(), {params: {start: 0, limit: 20}, baseURL: geoStoreUrl });
API.getRecords(url, startPosition, maxRecords, filter, options).then((result) => {
dispatch(recordsLoaded({
url,
startPosition,
maxRecords,
filter
}, result));
}).catch((e) => {
dispatch(recordsLoadError(e));
});
};
}
function textSearch(url, startPosition, maxRecords, text, options) {
return (dispatch /* , getState */) => {
// TODO auth (like) let opts = GeoStoreApi.getAuthOptionsFromState(getState(), {params: {start: 0, limit: 20}, baseURL: geoStoreUrl });
API.textSearch(url, startPosition, maxRecords, text, options).then((result) => {
dispatch(recordsLoaded({
url,
startPosition,
maxRecords,
text
}, result));
}).catch((e) => {
dispatch(recordsLoadError(e));
});
};
}

module.exports = {RECORD_LIST_LOADED, RECORD_LIST_LOAD_ERROR, getRecords, textSearch};
138 changes: 138 additions & 0 deletions web/client/api/CSW.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**
* 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 axios = require('../libs/ajax');

const {CSW, marshaller, unmarshaller} = require('../utils/ogc/CSW');
const {Filter} = require('../utils/ogc/Filter');

const _ = require('lodash');


/**
* API for local config
*/
var Api = {
getRecords: function(url, startPosition, maxRecords, filter) {
let body = marshaller.marshalString({
name: "csw:GetRecords",
value: CSW.getRecords(startPosition, maxRecords, filter)
});
return axios.post(url, body, { headers: {
'Content-Type': 'application/xml'
}}).then(
(response) => {
if (response ) {
let json = unmarshaller.unmarshalString(response.data);
if (json && json.name && json.name.localPart === "GetRecordsResponse" && json.value && json.value.searchResults) {
let rawResult = json.value;
let rawRecords = rawResult.searchResults.abstractRecord || rawResult.searchResults.any;
let result = {
numberOfRecordsMatched: rawResult.searchResults.numberOfRecordsMatched,
numberOfRecordsReturned: rawResult.searchResults.numberOfRecordsReturned,
nextRecord: rawResult.searchResults.nextRecord
// searchStatus: rawResult.searchStatus
};
let records = [];

if (rawRecords) {
for (let i = 0; i < rawRecords.length; i++) {
let rawRec = rawRecords[i].value;
let obj = {
dateStamp: rawRec.dateStamp && rawRec.dateStamp.date,
fileIdentifier: rawRec.fileIdentifier && rawRec.fileIdentifier.characterString && rawRec.fileIdentifier.characterString.value,
identificationInfo: rawRec.abstractMDIdentification && rawRec.abstractMDIdentification.value
};
if (rawRec.boundingBox) {
let bbox;
let crs;
let el;
if (Array.isArray(rawRec.boundingBox)) {
el = _.head(rawRec.boundingBox);
} else {
el = rawRec.boundingBox;
}
if (el && el.value) {
let lc = el.value.lowerCorner;
let uc = el.value.upperCorner;
bbox = [lc[1], lc[0], uc[1], uc[0]];
// TODO parse the extent's crs
let crsCode = el.value.crs.split(":::")[1];
if (crsCode === "WGS 1984") {
crs = "EPSG:4326";
} else if (crsCode) {
// TODO check is valid EPSG code
crs = "EPSG:" + crsCode;
} else {
crs = "EPSG:4326";
}
}
obj.boundingBox = {
extent: bbox,
crs: crs
};

}
let dcElement = rawRec.dcElement;
if (dcElement) {
let dc = {
};
for (let j = 0; j < dcElement.length; j++) {
let dcel = dcElement[j];
let elName = dcel.name.localPart;
let finalEl = {};
/* Some services (e.g. GeoServer ) support http://schemas.opengis.net/csw/2.0.2/record.xsd only
* Usually they publish the WMS URL at dct:"references" with scheme=OGC:WMS
* So we place references as they are.
*/
if (elName === "references" && dcel.value) {
let urlString = dcel.value.content && dcel.value.content[0] || dcel.value.content || dcel.value;

finalEl = {
value: urlString,
scheme: dcel.value.scheme
};
} else {
finalEl = dcel.value.content && dcel.value.content[0] || dcel.value.content || dcel.value;
}

if (dc[elName] && Array.isArray(dc[elName])) {
dc[elName].push(finalEl);
} else if (dc[elName]) {
dc[elName] = [dc[elName], finalEl];
} else {
dc[elName] = finalEl;
}
}
obj.dc = dc;

}
records.push(obj);

}
}
result.records = records;
return result;
}
}
return null;

});
},
textSearch: function(url, startPosition, maxRecords, text) {
// creates a request like this:
// <ogc:PropertyName>apiso:AnyText</ogc:PropertyName><ogc:Literal>%a%</ogc:Literal></ogc:PropertyIsLike>
let filter = null;
if (text) {
let ops = Filter.propertyIsLike("csw:AnyText", "%" + text + "%");
filter = Filter.filter(ops);
}
return Api.getRecords(url, startPosition, maxRecords, filter);
}
};

module.exports = Api;
59 changes: 59 additions & 0 deletions web/client/components/catalog/RecordGrid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* 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 React = require('react');

const {Grid, Row, Col} = require('react-bootstrap');

const RecordItem = require('./RecordItem');


const RecordGrid = React.createClass({
propTypes: {
recordItem: React.PropTypes.element,
catalogURL: React.PropTypes.string,
onZoomToExtent: React.PropTypes.func,
onLayerAdd: React.PropTypes.func,
records: React.PropTypes.array,
style: React.PropTypes.object
},
getDefaultProps() {
return {
records: [],
onLayerAdd: () => {}
};
},
renderRecordItem(record) {
let Item = this.props.recordItem || RecordItem;
return (
<Col xs={12} sm={6} md={6} lg={6} key={record.identifier}>
<Item
onLayerAdd={this.props.onLayerAdd}
onZoomToExtent={this.props.onZoomToExtent}
catalogURL={this.props.catalogURL}
record={record}
style={{height: "215px", maxHeight: "215px"}}/>
</Col>
);
},
render() {
if (this.props.records) {
let mapsList = this.props.records instanceof Array ? this.props.records : [this.props.records];
return (
<Grid className="record-grid" style={this.props.style}>
<Row>
{mapsList.map(this.renderRecordItem)}
</Row>
</Grid>
);
}

return null;
}
});

module.exports = RecordGrid;
Loading

0 comments on commit 66d454e

Please sign in to comment.