Skip to content

Commit 055c19a

Browse files
committed
first commit
0 parents  commit 055c19a

10 files changed

+533
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
node_modules

cswrequests.js

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { parseXml, evaluateXPath } from "./parsexml.js";
2+
3+
const nodeValue = (nodes, index) => nodes[index] ? nodes[index].nodeValue : undefined;
4+
const nodeArray = (nodes) => nodes?.length ? Array.from(nodes).map(node => node.nodeValue) : [];
5+
const nodeExists = (nodes) => nodes?.length ? true : false;
6+
7+
// Function to fetch and parse the GetCapabilities response
8+
export async function fetchGetCapabilities(url) {
9+
try {
10+
const response = await fetch(`${url}?service=CSW&request=GetCapabilities`);
11+
if (!response.ok) {
12+
throw new Error(`HTTP error! status: ${response.status}`);
13+
}
14+
const text = await response.text();
15+
16+
const cswCapabilities = await parseXml(text);
17+
const serviceTitle = nodeValue(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:Title/text()'), 0);
18+
const serviceAbstract = nodeValue(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:Abstract/text()'), 0);
19+
const fees = nodeValue(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:Fees/text()'), 0);
20+
const accessConstraints = nodeValue(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:AccessConstraints/text()'), 0);
21+
const serviceType = nodeValue(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:ServiceType/text()'), 0);
22+
const serviceTypeVersion = nodeValue(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:ServiceTypeVersion/text()'), 0);
23+
const keywords = nodeArray(evaluateXPath(cswCapabilities, '//ows:ServiceIdentification/ows:Keywords/ows:Keyword/text()'));
24+
const supportedISOQueryables = nodeArray(evaluateXPath(cswCapabilities, '//csw:Capabilities/ows:OperationsMetadata/ows:Operation[@name="GetRecords"]/ows:Constraint[@name="SupportedISOQueryables"]/ows:Value/text()'));
25+
const additionalQueryables = nodeArray(evaluateXPath(cswCapabilities, '//csw:Capabilities/ows:OperationsMetadata/ows:Operation[@name="GetRecords"]/ows:Constraint[@name="AdditionalQueryables"]/ows:Value/text()'));
26+
const geometryOperands = nodeArray(evaluateXPath(cswCapabilities, '//csw:Capabilities/ogc:Filter_Capabilities/ogc:Spatial_Capabilities/ogc:GeometryOperands/ogc:GeometryOperand/text()'));
27+
const spatialOperators = nodeArray(evaluateXPath(cswCapabilities, '//csw:Capabilities/ogc:Filter_Capabilities/ogc:Spatial_Capabilities/ogc:SpatialOperators/ogc:SpatialOperator/@name'));
28+
const comparisonOperators = nodeArray(evaluateXPath(cswCapabilities, '//csw:Capabilities/ogc:Filter_Capabilities/ogc:Scalar_Capabilities/ogc:ComparisonOperators/ogc:ComparisonOperator/text()'));
29+
const logicalOperators = nodeExists(evaluateXPath(cswCapabilities, '//csw:Capabilities/ogc:Filter_Capabilities/ogc:Scalar_Capabilities/ogc:LogicalOperators'));
30+
return {
31+
serviceTitle,
32+
serviceAbstract,
33+
fees,
34+
accessConstraints,
35+
serviceType,
36+
serviceTypeVersion,
37+
keywords,
38+
supportedISOQueryables,
39+
additionalQueryables,
40+
geometryOperands,
41+
spatialOperators,
42+
comparisonOperators,
43+
logicalOperators,
44+
};
45+
} catch (error) {
46+
console.error('Failed to fetch GetCapabilities:', error);
47+
return {
48+
error: error.message,
49+
serviceTitle: error.message,
50+
};
51+
}
52+
}
53+
54+
function bareTypeName(recordTypeName) {
55+
if (recordTypeName && recordTypeName.length) {
56+
let result = recordTypeName[0];
57+
if (result.indexOf(':') > -1) {
58+
return result.split(':')[1];
59+
}
60+
return result;
61+
}
62+
return '';
63+
}
64+
65+
export async function fetchDescribeRecord(url, cswVersion) {
66+
try {
67+
const paramKeys = ['service', 'request', 'version'];
68+
const preparedUrl = new URL(url);
69+
const params = preparedUrl.searchParams;
70+
params.forEach((value, key) => {
71+
if (paramKeys.includes(key.toLowerCase())) {
72+
params.delete(key);
73+
}
74+
});
75+
params.set('service', 'CSW');
76+
params.set('request', 'DescribeRecord');
77+
params.set('version', cswVersion);
78+
79+
const response = await fetch(preparedUrl.href);
80+
if (!response.ok) {
81+
throw new Error(`HTTP error! status: ${response.status}`);
82+
}
83+
const text = await response.text();
84+
85+
86+
87+
const cswDescribeRecord = await parseXml(text, {});
88+
89+
const briefRecordTypeName = bareTypeName(nodeArray(evaluateXPath(cswDescribeRecord, '//xs:element[@name="BriefRecord"]/@type')));
90+
const briefRecordType = nodeArray(evaluateXPath(cswDescribeRecord, `//xs:complexType[@name="${briefRecordTypeName}"]/xs:complexContent/xs:extension/xs:sequence/xs:element/@ref`));
91+
const summaryRecordTypeName = bareTypeName(nodeArray(evaluateXPath(cswDescribeRecord, '//xs:element[@name="SummaryRecord"]/@type')))
92+
const recordTypeName = bareTypeName(nodeArray(evaluateXPath(cswDescribeRecord, '//xs:element[@name="Record"]/@type')));
93+
94+
const summaryRecord = {
95+
cswVersion,
96+
recordTypes: nodeArray(evaluateXPath(cswDescribeRecord, '//csw:DescribeRecordResponse/csw:SchemaComponent/xs:schema/xs:element/@name')),
97+
recordFields: nodeArray(evaluateXPath(cswDescribeRecord, '//xs:element[@name="Record"]/xs:complexType/xs:sequence/xs:element/@name')),
98+
summaryRecordFields: nodeArray(evaluateXPath(cswDescribeRecord, '//xs:element[@name="SummaryRecord"]/xs:complexType/xs:sequence/xs:element/@name')),
99+
briefRecordFields: briefRecordType
100+
};
101+
102+
return {
103+
summaryRecord,
104+
};
105+
} catch (error) {
106+
console.error('Failed to fetch DescribeRecord:', error);
107+
return {
108+
error: error.message
109+
};
110+
}
111+
}
112+

endpoints.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const cswEndPoints = [
2+
{name: "geonorge", url:"https://www.geonorge.no/geonetwork/srv/nor/csw"},
3+
{name: "nationaalgeoregister", url: "https://nationaalgeoregister.nl/geonetwork/srv/dut/csw-inspire"},
4+
{name: "linz", url: "https://data.linz.govt.nz/services/csw?service=CSW&version=2.0.2&request=GetRecords"},
5+
{name: "geo-solutions", url: "https://gs-stable.geo-solutions.it/geoserver/csw"},
6+
{name: "ign-fr", url: "https://data.geopf.fr/csw"},
7+
]

index.js

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
import xpath from 'xpath';
2+
import xmldom from 'xmldom';
3+
4+
//const cswUrl = 'https://nationaalgeoregister.nl/geonetwork/srv/dut/csw-inspire';
5+
const cswUrl = 'https://data.linz.govt.nz/services/csw?service=CSW&version=2.0.2&request=GetRecords';
6+
const briefTitleSearch = `<?xml version="1.0" encoding="UTF-8"?>
7+
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
8+
service="CSW"
9+
version="2.0.2"
10+
resultType="results"
11+
startPosition="1"
12+
maxRecords="10"
13+
xmlns:ogc="http://www.opengis.net/ogc"
14+
xmlns:gml="http://www.opengis.net/gml"
15+
xmlns:dc="http://purl.org/dc/elements/1.1/"
16+
xmlns:dct="http://purl.org/dc/terms/"
17+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
18+
xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2
19+
http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
20+
<csw:Query typeNames="csw:Record">
21+
<csw:ElementSetName>brief</csw:ElementSetName>
22+
<csw:Constraint version="1.1.0">
23+
<ogc:Filter>
24+
<ogc:PropertyIsLike wildCard="%" singleChar="_" escapeChar="\">
25+
<ogc:PropertyName>dc:title</ogc:PropertyName>
26+
<ogc:Literal>%water%</ogc:Literal>
27+
</ogc:PropertyIsLike>
28+
</ogc:Filter>
29+
</csw:Constraint>
30+
</csw:Query>
31+
</csw:GetRecords>`;
32+
33+
const fullAnySearch = `<?xml version="1.0" encoding="UTF-8"?>
34+
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
35+
service="CSW"
36+
version="2.0.2"
37+
resultType="results"
38+
startPosition="1"
39+
maxRecords="10"
40+
xmlns:ogc="http://www.opengis.net/ogc"
41+
xmlns:gml="http://www.opengis.net/gml"
42+
xmlns:dc="http://purl.org/dc/elements/1.1/"
43+
xmlns:dct="http://purl.org/dc/terms/"
44+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
45+
xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2
46+
http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
47+
<csw:Query typeNames="csw:Record">
48+
<csw:ElementSetName>full</csw:ElementSetName>
49+
<csw:Constraint version="1.1.0">
50+
<ogc:Filter>
51+
<ogc:And>
52+
<ogc:PropertyIsLike wildCard="%" singleChar="_" escapeChar="\\">
53+
<ogc:PropertyName>AnyText</ogc:PropertyName>
54+
<ogc:Literal>%water%</ogc:Literal>
55+
</ogc:PropertyIsLike>
56+
<ogc:PropertyIsEqualTo>
57+
<ogc:PropertyName>type</ogc:PropertyName>
58+
<ogc:Literal>service</ogc:Literal>
59+
</ogc:PropertyIsEqualTo>
60+
</ogc:And>
61+
</ogc:Filter>
62+
</csw:Constraint>
63+
</csw:Query>
64+
</csw:GetRecords>`
65+
66+
const serviceSearch = `<?xml version="1.0" encoding="UTF-8"?>
67+
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
68+
service="CSW"
69+
version="2.0.2"
70+
resultType="results"
71+
startPosition="1"
72+
maxRecords="10"
73+
xmlns:ogc="http://www.opengis.net/ogc"
74+
xmlns:gml="http://www.opengis.net/gml"
75+
xmlns:dc="http://purl.org/dc/elements/1.1/"
76+
xmlns:dct="http://purl.org/dc/terms/"
77+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
78+
xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2
79+
http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
80+
<csw:Query typeNames="csw:Record">
81+
<csw:ElementSetName>full</csw:ElementSetName>
82+
<csw:Constraint version="1.1.0">
83+
<ogc:Filter>
84+
<ogc:PropertyIsEqualTo>
85+
<ogc:PropertyName>apiso:Type</ogc:PropertyName>
86+
<ogc:Literal>service</ogc:Literal>
87+
</ogc:PropertyIsEqualTo>
88+
</ogc:Filter>
89+
</csw:Constraint>
90+
</csw:Query>
91+
</csw:GetRecords>`
92+
93+
const extentSearch = `<?xml version="1.0" encoding="UTF-8"?>
94+
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
95+
service="CSW"
96+
version="2.0.2"
97+
resultType="results"
98+
startPosition="1"
99+
maxRecords="10"
100+
xmlns:ogc="http://www.opengis.net/ogc"
101+
xmlns:gml="http://www.opengis.net/gml"
102+
xmlns:dc="http://purl.org/dc/elements/1.1/"
103+
xmlns:dct="http://purl.org/dc/terms/"
104+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
105+
xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2
106+
http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
107+
<csw:Query typeNames="csw:Record">
108+
<csw:ElementSetName>full</csw:ElementSetName>
109+
<csw:Constraint version="1.1.0">
110+
<ogc:Filter>
111+
<ogc:And>
112+
<ogc:PropertyIsLike wildCard="%" singleChar="_" escapeChar="\\">
113+
<ogc:PropertyName>AnyText</ogc:PropertyName>
114+
<ogc:Literal>%water%</ogc:Literal>
115+
</ogc:PropertyIsLike>
116+
<ogc:PropertyIsEqualTo>
117+
<ogc:PropertyName>type</ogc:PropertyName>
118+
<ogc:Literal>service</ogc:Literal>
119+
</ogc:PropertyIsEqualTo>
120+
<ogc:BBOX>
121+
<ogc:PropertyName>ows:BoundingBox</ogc:PropertyName>
122+
<gml:Envelope>
123+
<gml:lowerCorner>4.811 52.273</gml:lowerCorner>
124+
<gml:upperCorner>5.003 52.425</gml:upperCorner>
125+
</gml:Envelope>
126+
</ogc:BBOX>
127+
</ogc:And>
128+
</ogc:Filter>
129+
</csw:Constraint>
130+
</csw:Query>
131+
</csw:GetRecords>`;
132+
133+
const withinExtentSearch = `<?xml version="1.0" encoding="UTF-8"?>
134+
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2"
135+
service="CSW"
136+
version="2.0.2"
137+
resultType="results"
138+
startPosition="1"
139+
maxRecords="10"
140+
xmlns:ogc="http://www.opengis.net/ogc"
141+
xmlns:gml="http://www.opengis.net/gml"
142+
xmlns:dc="http://purl.org/dc/elements/1.1/"
143+
xmlns:dct="http://purl.org/dc/terms/"
144+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
145+
xsi:schemaLocation="http://www.opengis.net/cat/csw/2.0.2
146+
http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd">
147+
<csw:Query typeNames="csw:Record">
148+
<csw:ElementSetName>full</csw:ElementSetName>
149+
<csw:Constraint version="1.1.0">
150+
<ogc:Filter>
151+
<ogc:And>
152+
<ogc:PropertyIsLike wildCard="%" singleChar="_" escapeChar="\\">
153+
<ogc:PropertyName>AnyText</ogc:PropertyName>
154+
<ogc:Literal>%water%</ogc:Literal>
155+
</ogc:PropertyIsLike>
156+
<ogc:PropertyIsEqualTo>
157+
<ogc:PropertyName>type</ogc:PropertyName>
158+
<ogc:Literal>service</ogc:Literal>
159+
</ogc:PropertyIsEqualTo>
160+
<ogc:Within>
161+
<ogc:PropertyName>ows:BoundingBox</ogc:PropertyName>
162+
<gml:Envelope>
163+
<gml:lowerCorner>0.91 50.17</gml:lowerCorner>
164+
<gml:upperCorner>9.64 54.15</gml:upperCorner>
165+
</gml:Envelope>
166+
</ogc:Within>
167+
</ogc:And>
168+
</ogc:Filter>
169+
</csw:Constraint>
170+
</csw:Query>
171+
</csw:GetRecords>`
172+
173+
fetch(cswUrl, {
174+
method: 'POST',
175+
headers: {
176+
'Content-Type': 'application/xml',
177+
},
178+
body: serviceSearch
179+
})
180+
.then(response => response.text()) // Assuming the response is XML, get it as text
181+
.then(str => {
182+
//console.log(str);
183+
return (new xmldom.DOMParser()).parseFromString(str, "text/xml")})
184+
.then(data => {
185+
// Process the XML data here
186+
// For example, extracting titles from the response
187+
//const titles = data.querySelectorAll('dc\\:title, title');
188+
const select = xpath.useNamespaces({
189+
"dc": "http://purl.org/dc/elements/1.1/",
190+
"csw": "http://www.opengis.net/cat/csw/2.0.2"
191+
});
192+
const records = select('//csw:Record', data);
193+
console.log(`Number of records found: ${records.length}`)
194+
records.forEach(record => {
195+
const title = select('.//dc:title', record)[0].textContent;
196+
const uri = select('.//dc:URI', record)[0].textContent;
197+
console.log(`Title: ${title}, URI: ${uri}`);
198+
});
199+
})
200+
.catch(error => console.error('Error:', error));

notes.txt

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
geoserver has a csw plugin to publish metadata about its services
2+
3+
pycsw is used by other services
4+
5+
mapstore has a CSW parser, but to get service urls, it uses record attribute dct:references, for example:
6+
<dct:references scheme="OGC:WMS">https://gs-stable.geosolutionsgroup.com/geoserver/wms?service=WMS&request=GetMap&layers=test:Linea_costa</dct:references>
7+
and publishes <dt:type> to be: http://purl.org/dc/dcmitype/Dataset
8+
<dc:type>http://purl.org/dc/dcmitype/Dataset</dc:type>
9+
It seems this CSW record is created by geoserver
10+
11+
nationaalgeoregister uses dc:URI for service references
12+
<dc:URI protocol="" description="accessPoint">https://service.pdok.nl/cbs/vierkantstatistieken100m/2007/wfs/v1_0?request=GetCapabilities&service=WFS</dc:URI>
13+
14+
OpenSearch is an alternative? to CSW

package-lock.json

+33
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "cswclient",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"type": "module",
7+
"scripts": {
8+
"test": "echo \"Error: no test specified\" && exit 1"
9+
},
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"xmldom": "^0.6.0",
14+
"xpath": "^0.0.34"
15+
}
16+
}

0 commit comments

Comments
 (0)