1 | import { isISODateAxis, isLongitudeAxis, getLongitudeWrapper, asTime } from '../domain/referencing.js'
|
2 | import { normalizeIndexSubsetConstraints, subsetDomainByIndex } from '../domain/subset.js'
|
3 | import { indexOfNearest, indicesOfNearest } from '../array.js'
|
4 | import { COVERAGE } from '../constants.js'
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 | export function subsetByBbox (cov, bbox, axes = ['x', 'y']) {
|
17 | let [xmin, ymin, xmax, ymax] = bbox
|
18 | return cov.subsetByValue({[axes[0]]: {start: xmin, stop: xmax}, [axes[1]]: {start: ymin, stop: ymax}})
|
19 | }
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | export function subsetByIndex (cov, constraints) {
|
32 | return cov.loadDomain().then(domain => {
|
33 | constraints = normalizeIndexSubsetConstraints(domain, constraints)
|
34 | let newdomain = subsetDomainByIndex(domain, constraints)
|
35 |
|
36 |
|
37 | let rangeWrapper = range => {
|
38 | let newrange = {
|
39 | dataType: range.dataType,
|
40 | get: obj => {
|
41 |
|
42 | let newobj = {}
|
43 | for (let axisName of Object.keys(obj)) {
|
44 | let {start, step} = constraints[axisName]
|
45 | newobj[axisName] = start + obj[axisName] * step
|
46 | }
|
47 | return range.get(newobj)
|
48 | }
|
49 | }
|
50 | newrange.shape = new Map()
|
51 | for (let axisName of domain.axes.keys()) {
|
52 | let size = newdomain.axes.get(axisName).values.length
|
53 | newrange.shape.set(axisName, size)
|
54 | }
|
55 | return newrange
|
56 | }
|
57 |
|
58 | let loadRange = key => cov.loadRange(key).then(rangeWrapper)
|
59 |
|
60 | let loadRanges = keys => cov.loadRanges(keys).then(ranges => new Map([...ranges].map(([key, range]) => [key, rangeWrapper(range)]))
|
61 | )
|
62 |
|
63 |
|
64 | let newcov = {
|
65 | type: COVERAGE,
|
66 | domainType: cov.domainType,
|
67 | parameters: cov.parameters,
|
68 | loadDomain: () => Promise.resolve(newdomain),
|
69 | loadRange,
|
70 | loadRanges
|
71 | }
|
72 | newcov.subsetByIndex = subsetByIndex.bind(null, newcov)
|
73 | newcov.subsetByValue = subsetByValue.bind(null, newcov)
|
74 | return newcov
|
75 | })
|
76 | }
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | export function subsetByValue (cov, constraints) {
|
90 | return cov.loadDomain().then(domain => {
|
91 |
|
92 | let indexConstraints = {}
|
93 |
|
94 | for (let axisName of Object.keys(constraints)) {
|
95 | let spec = constraints[axisName]
|
96 | if (spec === undefined || spec === null || !domain.axes.has(axisName)) {
|
97 | continue
|
98 | }
|
99 | let axis = domain.axes.get(axisName)
|
100 | let vals = axis.values
|
101 |
|
102 |
|
103 | let isISODate = isISODateAxis(domain, axisName)
|
104 | let isLongitude = isLongitudeAxis(domain, axisName)
|
105 |
|
106 |
|
107 | let lonWrapper = isLongitude ? getLongitudeWrapper(domain, axisName) : undefined
|
108 |
|
109 | if (typeof spec === 'number' || typeof spec === 'string' || spec instanceof Date) {
|
110 | let match = spec
|
111 | if (isISODate) {
|
112 |
|
113 | match = asTime(match)
|
114 | vals = vals.map(v => new Date(v).getTime())
|
115 | } else if (isLongitude) {
|
116 | match = lonWrapper(match)
|
117 | }
|
118 | let i
|
119 |
|
120 | if (vals.indexOf) {
|
121 | i = vals.indexOf(match)
|
122 | } else {
|
123 | i = Array.prototype.indexOf.call(vals, match)
|
124 | }
|
125 | if (i === -1) {
|
126 | throw new Error('Domain value not found: ' + spec)
|
127 | }
|
128 | indexConstraints[axisName] = i
|
129 | } else if ('target' in spec) {
|
130 |
|
131 | let target = spec.target
|
132 | if (isISODate) {
|
133 |
|
134 | target = asTime(target)
|
135 | vals = vals.map(v => new Date(v).getTime())
|
136 | } else if (isLongitude) {
|
137 | target = lonWrapper(target)
|
138 | } else if (typeof vals[0] !== 'number' || typeof target !== 'number') {
|
139 | throw new Error('Invalid axis or constraint value type')
|
140 | }
|
141 | let i = indexOfNearest(vals, target)
|
142 | indexConstraints[axisName] = i
|
143 | } else if ('start' in spec && 'stop' in spec) {
|
144 |
|
145 |
|
146 | let {start, stop} = spec
|
147 | if (isISODate) {
|
148 |
|
149 | [start, stop] = [asTime(start), asTime(stop)]
|
150 | vals = vals.map(v => new Date(v).getTime())
|
151 | } else if (isLongitude) {
|
152 | [start, stop] = [lonWrapper(start), lonWrapper(stop)]
|
153 | } else if (typeof vals[0] !== 'number' || typeof start !== 'number') {
|
154 | throw new Error('Invalid axis or constraint value type')
|
155 | }
|
156 |
|
157 | let [lo1, hi1] = indicesOfNearest(vals, start)
|
158 | let [lo2, hi2] = indicesOfNearest(vals, stop)
|
159 |
|
160 |
|
161 |
|
162 | let imin = Math.min(lo1, hi1, lo2, hi2)
|
163 | let imax = Math.max(lo1, hi1, lo2, hi2) + 1
|
164 |
|
165 | indexConstraints[axisName] = {start: imin, stop: imax}
|
166 | } else {
|
167 | throw new Error('Invalid subset constraints')
|
168 | }
|
169 | }
|
170 |
|
171 | return cov.subsetByIndex(indexConstraints)
|
172 | })
|
173 | }
|