UNPKG

3.71 kBJavaScriptView Raw
1import { COVERAGECOLLECTION } from '../constants.js'
2import { asTime, isISODateAxis, isLongitudeAxis, getLongitudeWrapper } from '../domain/referencing.js'
3
4/**
5 * Adds a basic query() function to the coverage collection object.
6 * Note that this does not support paging.
7 */
8export function addCollectionQueryFunction (collection) {
9 if (collection.paging) {
10 throw new Error('Paged collections not supported')
11 }
12 collection.query = () => new CollectionQuery(collection)
13}
14
15export class CollectionQuery {
16 /**
17 * @param {CoverageCollection} collection
18 */
19 constructor (collection) {
20 this._collection = collection
21 this._filter = {}
22 this._subset = {}
23 }
24
25 /**
26 * Matching mode: intersect
27 *
28 * Supports ISO8601 date string axes.
29 * All other string-type axes are compared alphabetically.
30 *
31 * @example
32 * collection.query().filter({
33 * 't': {start: '2015-01-01T01:00:00', stop: '2015-01-01T02:00:00'}
34 * }).execute().then(filteredCollection => {
35 * console.log(filteredCollection.coverages.length)
36 * })
37 * @param {Object} spec
38 * @return {CollectionQuery}
39 */
40 filter (spec) {
41 mergeInto(spec, this._filter)
42 return this
43 }
44
45 /**
46 * Subset coverages by domain values.
47 *
48 * Equivalent to calling {@link Coverage.subsetByValue}(spec) on each
49 * coverage in the collection.
50 *
51 * @param {Object} spec
52 * @return {CollectionQuery}
53 */
54 subset (spec) {
55 mergeInto(spec, this._subset)
56 return this
57 }
58
59 /**
60 * Applies the query operators and returns
61 * a Promise that succeeds with a new CoverageCollection.
62 *
63 * @return {Promise<CoverageCollection>}
64 */
65 execute () {
66 let coll = this._collection
67 let newcoll = {
68 type: COVERAGECOLLECTION,
69 coverages: [],
70 parameters: coll.parameters,
71 domainType: coll.domainType
72 }
73
74 let promises = []
75 for (let cov of coll.coverages) {
76 promises.push(cov.loadDomain().then(domain => {
77 if (!matchesFilter(domain, this._filter)) {
78 return
79 }
80
81 if (Object.keys(this._subset).length === 0) {
82 newcoll.coverages.push(cov)
83 } else {
84 return cov.subsetByValue(this._subset).then(subsetted => {
85 newcoll.coverages.push(subsetted)
86 })
87 }
88 }))
89 }
90 return Promise.all(promises).then(() => {
91 newcoll.query = () => new CollectionQuery(newcoll)
92 return newcoll
93 })
94 }
95}
96
97function matchesFilter (domain, filter) {
98 for (let axisName of Object.keys(filter)) {
99 let condition = filter[axisName]
100 if (!domain.axes.has(axisName)) {
101 throw new Error('Axis "' + axisName + '" does not exist')
102 }
103 let axis = domain.axes.get(axisName)
104 let vals = axis.values
105
106 let [min, max] = [vals[0], vals[vals.length - 1]]
107 if (typeof min !== 'number' && typeof min !== 'string') {
108 throw new Error('Can only filter primitive axis values')
109 }
110 let {start, stop} = condition
111
112 // special handling
113 if (isISODateAxis(domain, axisName)) {
114 [min, max] = [asTime(min), asTime(max)]
115 ;[start, stop] = [asTime(start), asTime(stop)]
116 } else if (isLongitudeAxis(domain, axisName)) {
117 let lonWrapper = getLongitudeWrapper(domain, axisName)
118 ;[start, stop] = [lonWrapper(start), lonWrapper(stop)]
119 }
120
121 if (min > max) {
122 [min, max] = [max, min]
123 }
124 if (max < start || stop < min) {
125 return false
126 }
127 }
128
129 return true
130}
131
132function mergeInto (inputObj, targetObj) {
133 for (let k of Object.keys(inputObj)) {
134 targetObj[k] = inputObj[k]
135 }
136}