-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathhighlight.mjs
274 lines (191 loc) · 6.89 KB
/
highlight.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
let _this;
export default function(params){
// Finish the current interaction.
this.interaction?.finish()
_this = Object.assign({
mapview: this,
type: 'highlight',
finish,
clear,
highlight,
getLocation,
hitTolerance: 5,
candidates: {},
candidateKeys: new Set(),
locations: new Set(),
// Filter for layers which have a highlight style.
layerFilter: L => Object.values(this.layers)
// layer L matching the feature layer L must have a qID defined
.some(layer => layer.qID && layer.L === L)
}, params)
// Set _this to be the current mapview _this.
_this.mapview.interaction = _this
// pointerMove will highlight features.
_this.mapview.Map.on('pointermove', pointerMove)
// click will select the highlighted feature.
_this.mapview.Map.on('click', click)
let shortCircuit
function pointerMove(e) {
// Method should short circuit if still processing.
if (shortCircuit) return;
// Set shortCircuit flag.
shortCircuit = true;
let candidates = {};
_this.mapview.Map.forEachFeatureAtPixel(e.pixel,
(F, L) => {
// Create candidate object.
let candidate = {
key: `${L.get('key') || ol.util.getUid(L)}!${F.get('id') || F.getId() || ol.util.getUid(F)}`,
F, L
}
// Assign to key in candidates object.
candidates[candidate.key] = candidate
},
{
layerFilter: _this.layerFilter,
hitTolerance: _this.hitTolerance,
})
// No features at pixel.
if (!Object.keys(candidates).length) {
// Reset candidateKeys
_this.candidateKeys = new Set();
// Remove shortCircuit flag.
shortCircuit = false;
// Clear highlight.
clear();
// There is nothing to highlight.
return;
}
// Check whether set of candidate keys is equal to _this.candidateKeys
if (mapp.utils.areSetsEqual(_this.candidateKeys, new Set(Object.keys(candidates)))) {
// Remove shortCircuit flag
shortCircuit = false;
// The highlight hasn't changed.
return;
}
// Find candidate from key which is not in candidates set.
let candidate = candidates[Object.keys(candidates).find(key => !_this.candidateKeys.has(key))]
// Assign candidate from first key if candidate is falsy.
candidate = candidate
|| _this.current?.key && candidates[_this.current?.key]
|| candidates[Object.keys(candidates)[0]]
// Assign new Set of candidate keys to _this.
_this.candidateKeys = new Set(Object.keys(candidates))
// Remove shortCircuit flag.
shortCircuit = false;
// Call highlight method.
_this.highlight(candidate, e)
}
function highlight(feature, e) {
// Highlight method should only be called if the highlight has changed.
clear()
// Assign the layer and ID to the candidate object.
_this.current = Object.assign(feature, {
layer: _this.mapview.layers[feature.L.get('key')],
id: feature.F.get('id') || feature.F.getId()
})
// Assign the id to the layer highlight key.
// Required to determine in the style function whether the current feature should be styled as highlighted.
_this.current.layer.highlight = feature.id
// Touch events should not highlight features since there is no cursor.
if (e.originalEvent.pointerType !== 'mouse') {
// Don't get location from touch pan or zoom pinch event.
e.type !== 'pointermove' && _this.getLocation(_this.current)
// Clear touch highlight.
clear()
return;
}
// Change cursor if the highlight is selectable.
if (_this.current.layer.infoj) _this.mapview.Map.getTargetElement().style.cursor = 'pointer'
// Execute any hover method assigned to the current feature layer.
typeof _this.current.layer.hover?.show === 'function' && _this.current.layer.hover.show(feature.F)
// Style the feature itself if possible.
if (_this.current.layer.format !== 'mvt'
&& typeof _this.current.F.setStyle === 'function') {
feature.F.set('highlight', true)
_this.current.F.setStyle()
} else if (_this.current.layer.style.highlight) {
// Render unto canvas those things that are canvas'
_this.current.layer.L.changed()
}
}
let clicked;
// Select the current highlighted feature.
function click(e) {
// Limit click event to 600ms
if (clicked) return;
clicked = setTimeout(() => { clicked = null }, 600);
// There is no current highlighted feature without pointerMove.
// Simulate pointermove on the touch click coordinates.
if (e.originalEvent.pointerType === 'touch') {
_this.candidateKeys = new Set();
e.type = 'touchClick'
pointerMove(e)
return
}
// Remove any existing popup. e.g. Cluster select dialogue.
_this.mapview.popup(null)
// Return if there is no current highlight to select.
if (_this.current) {
_this.getLocation(_this.current);
return;
}
// Execute the noLocationClick method
if (typeof _this.noLocationClick === 'function') {
_this.noLocationClick(e)
}
}
function clear() {
// Highlight has already been cleared.
if (!_this.current) return;
// Remove any infotip.
_this.mapview.infotip(null)
// Reset cursor.
_this.mapview.Map.getTargetElement().style.cursor = 'auto'
// Delete the highlight id from current layer.
delete _this.current.layer.highlight
// Style the feature itself if possible.
if (_this.current.layer.format !== 'mvt'
&& typeof _this.current.F.setStyle === 'function') {
_this.current.F.set('highlight', false)
_this.current.F.setStyle()
} else if (_this.current.layer.style.highlight) {
// Render unto canvas those things that are canvas'
_this.current.layer.L.changed()
}
// Delete the current highlight object.
delete _this.current
}
function getLocation(feature) {
if (!feature.layer.infoj) return;
// Get the properties of the current highlight feature.
const properties = feature.F.getProperties()
// Return with a select dialogue for cluster feature.
if (properties.count > 1) {
mapp.location.nnearest({
mapview: _this.mapview,
layer: feature.layer,
table: feature.layer.table || feature.layer.tableCurrent(),
feature: feature.F
})
return;
}
// Select the current highlight feature.
mapp.location.get({
layer: feature.layer,
table: feature.layer.table || feature.layer.tableCurrent(),
id: feature.layer.highlight
})
}
// Finished the highlight _this.
function finish() {
// Set the current interaction to null.
_this.mapview.interaction = null
clear()
// Remove popup from mapview.
_this.mapview.popup(null)
// Remove event listener from mapview.
_this.mapview.Map.un('pointermove', pointerMove)
_this.mapview.Map.un('click', click)
}
}