Skip to content

Commit 50a4248

Browse files
committed
draft 1
draft 2 draft 3 draft 4 draft 5 draft 6 DBC22-6776: fixed broken test cases
1 parent 378e4cd commit 50a4248

6 files changed

Lines changed: 137 additions & 19 deletions

File tree

src/backend/apps/event/tests/test_data/event_feed_list_of_one.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
49.50658
2525
]
2626
},
27+
"location_description": "Quesnel-Hixon Road, in both directions. Landslide at Cottonwood bridge.",
2728
"route_at": "Highway 3",
2829
"route_from": "Pritchard Rd",
2930
"route_to": "",

src/backend/apps/event/tests/test_data/event_parsed_feed.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
[-120.526853, 49.451003],
2929
[-120.526427, 49.451752]
3030
])),
31+
("location_description", "Highway 3 between Bromley Pl and Frontage Rd"),
3132
("first_created", datetime.datetime(
3233
2023, 5, 19, 14, 29, 20, tzinfo=zoneinfo.ZoneInfo(key="America/Vancouver")
3334
)),
@@ -54,6 +55,7 @@
5455
("route_to", "Frontage Rd"),
5556
("direction", "NONE"),
5657
("location", Point(-120.526427, 49.451752)),
58+
("location_description", "Highway 3 between Bromley Pl and Frontage Rd"),
5759
# ("location", {"coordinates": [-120.526427, 49.451752]})
5860
("coordinates", [-120.526427, 49.451752]),
5961
("first_created", datetime.datetime(

src/frontend/src/Components/map/handlers/click.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ const camClickHandler = (
254254
feature.setCameraStyle('active');
255255
feature.set('clicked', true, true);
256256
feature.set('unread', false);
257+
feature.set('hovered', false);
257258

258259
if (isCamDetail) {
259260
// Do not auto center on feature on cam details page
@@ -574,8 +575,14 @@ export const pointerClickHandler = (
574575
mapContext
575576
) => {
576577
if (features.length) {
577-
const clickedFeature = features[0];
578-
switch (clickedFeature.getProperties()['type']) {
578+
let clickedFeature = features[0];
579+
580+
if (!clickedFeature.get('type')) {
581+
clickedFeature = features[0].values_?.features[0];
582+
}
583+
584+
585+
switch (clickedFeature?.getProperties()['type']) {
579586
case 'camera':
580587
trackEvent(
581588
'click',

src/frontend/src/Components/map/handlers/hover.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const resetHoveredStates = (targetFeature, hoveredFeatureRef) => {
3333
if (!hoveredFeature.getProperties().clicked) {
3434
switch (hoveredFeature.getProperties()['type']) {
3535
case 'camera':
36+
hoveredFeature.set('hovered', false);
3637
hoveredFeature.setCameraStyle('static');
3738
break;
3839
case 'event': {
@@ -145,15 +146,25 @@ export const pointerMoveHandler = (e, mapRef, hoveredFeature) => {
145146
});
146147

147148
if (features.length) {
148-
const targetFeature = features[0];
149-
resetHoveredStates(targetFeature, hoveredFeature);
150-
hoveredFeature.current = targetFeature;
149+
let targetFeature = features[0];
150+
if(targetFeature && !targetFeature.get('type')) {
151+
// targetFeature = features[0].values_?.features[0];
152+
const clusterFeatures = targetFeature.get('features');
151153

152-
// Set hover style if feature isn't clicked
153-
switch (targetFeature.getProperties()['type']) {
154+
if (clusterFeatures && clusterFeatures.length === 1) {
155+
targetFeature = clusterFeatures[0];
156+
}
157+
}
158+
159+
const realFeature = targetFeature;
160+
resetHoveredStates(realFeature, hoveredFeature);
161+
hoveredFeature.current = realFeature;
162+
163+
switch (realFeature?.get('type')) {
154164
case 'camera':
155-
if (!targetFeature.get('clicked')) {
156-
targetFeature.setCameraStyle('hover');
165+
if (!realFeature.get('clicked')) {
166+
realFeature.setCameraStyle('hover');
167+
realFeature.set('hovered', true);
157168
}
158169
return;
159170
case 'event':

src/frontend/src/Components/map/layers/camerasLayer.js

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// OpenLayers
22
import { Point } from 'ol/geom';
3-
import { Style } from 'ol/style';
3+
// import { Style } from 'ol/style';
4+
import {Style, Fill, Stroke, Text} from 'ol/style';
45
import * as ol from 'ol';
56
import VectorLayer from 'ol/layer/Vector';
67
import VectorSource from 'ol/source/Vector';
8+
import Cluster from 'ol/source/Cluster.js';
9+
import CircleStyle from 'ol/style/Circle.js';
710

811
// Styling
912
import { cameraStyles, unreadCameraStyles } from '../../data/featureStyleDefinitions.js';
1013

11-
export function getCamerasLayer(cameras, projectionCode, mapContext, referenceData, updateReferenceFeature, setLoadingLayers) {
14+
export function getCamerasLayer(cameras, projectionCode, mapContext, referenceData, updateReferenceFeature, setLoadingLayers, pixelDistance) {
1215
const vectorSource = new VectorSource();
1316

1417
cameras.forEach(camera => {
@@ -47,12 +50,80 @@ export function getCamerasLayer(cameras, projectionCode, mapContext, referenceDa
4750
}
4851
});
4952

50-
return new VectorLayer({
53+
const clusterSource = new Cluster({
54+
distance: pixelDistance,
55+
source: vectorSource,
56+
});
57+
58+
const styleCache = {};
59+
60+
const layer = new VectorLayer({
5161
classname: 'webcams',
5262
visible: mapContext.visible_layers.highwayCams,
53-
source: vectorSource,
54-
style: () => null
63+
source: clusterSource,
64+
65+
style: function (feature) {
66+
const features = feature.get('features');
67+
const size = features.length;
68+
69+
// single camera
70+
if (size === 1) {
71+
const cam = features[0];
72+
73+
if (cam.get('clicked')) {
74+
return cameraStyles.active;
75+
}
76+
if (cam.get('hovered')) {
77+
return cameraStyles.hover;
78+
}
79+
else {
80+
return cameraStyles.static;
81+
}
82+
}
83+
84+
// cluster
85+
let style = styleCache[size];
86+
87+
if (!style) {
88+
style = new Style({
89+
image: new CircleStyle({
90+
radius: 10,
91+
stroke: new Stroke({ color: '#fff' }),
92+
fill: new Fill({ color: '#3399CC' }),
93+
}),
94+
text: new Text({
95+
text: String(size),
96+
fill: new Fill({ color: '#fff' }),
97+
}),
98+
});
99+
100+
styleCache[size] = style;
101+
}
102+
103+
return style;
104+
},
55105
});
106+
107+
108+
109+
// expose clusterSource so we can control zoom behavior
110+
layer.getClusterSource = () => clusterSource;
111+
112+
113+
114+
return layer;
115+
116+
117+
118+
// return new VectorLayer({
119+
// classname: 'webcams',
120+
// visible: mapContext.visible_layers.highwayCams,
121+
// source: vectorSource,
122+
// style: () => null
123+
// });
124+
125+
126+
56127
}
57128

58129
export function updateCamerasLayer(cameras, layer, setLoadingLayers) {
@@ -62,7 +133,11 @@ export function updateCamerasLayer(cameras, layer, setLoadingLayers) {
62133
}, {});
63134

64135
for (const feature of layer.getSource().getFeatures()) {
65-
const camera = camerasLookup[feature.getId()];
136+
let camera = camerasLookup[feature.getId()];
137+
if (!camera) {
138+
camera = camerasLookup[feature.values_.features[0].getId()];
139+
}
140+
66141

67142
if (!camera) { // camera no longer in list from API
68143
feature.setStyle(new Style(null));
@@ -76,12 +151,17 @@ export function updateCamerasLayer(cameras, layer, setLoadingLayers) {
76151
feature.setProperties(camera); // update feature with latest API data.
77152

78153
if (feature.get('clicked')) { continue; }
79-
80-
feature.setCameraStyle('static');
154+
try {
155+
feature.setCameraStyle('static');
156+
}
157+
catch {
158+
feature.values_.features[0].setCameraStyle('static');
159+
}
160+
// feature.setCameraStyle('static');
81161
}
82162

83163
setLoadingLayers(prevState => ({
84164
...prevState,
85165
cameras: false
86166
}));
87-
}
167+
}

src/frontend/src/Components/map/layers/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,26 @@ const layerUpdateFuncMap = {
4141
dms: updateDmsLayer,
4242
}
4343

44+
const CLUSTER_DISTANCE_METERS = 178571; // 178 km converted to about 50 pixel on the map has the best effect when zoom in/out
45+
let pixelDistance = 50; // defalt pixel 50 has the best effect
46+
4447
export const loadLayer = (mapLayers, mapRef, mapContext, key, dataList, filteredDataList, zIndex, referenceData, updateReferenceFeature, setLoadingLayers) => {
4548
// Always remove and regenerate route layer
4649
if (key == 'routeLayer') {
4750
mapRef.current.removeLayer(mapLayers.current[key]);
4851
}
4952

53+
const view = mapRef.current.getView();
54+
55+
function updateClusterDistance() {
56+
const resolution = view.getResolution(); // meters per pixel
57+
// Convert meters to pixels
58+
pixelDistance = CLUSTER_DISTANCE_METERS / resolution;
59+
}
60+
61+
view.on('change:resolution', updateClusterDistance);
62+
updateClusterDistance();
63+
5064
if (dataList) {
5165
if (!mapLayers.current[key] || key == 'routeLayer') {
5266
// Generate and add layer if it doesn't exist
@@ -56,7 +70,8 @@ export const loadLayer = (mapLayers, mapRef, mapContext, key, dataList, filtered
5670
mapContext,
5771
referenceData,
5872
updateReferenceFeature,
59-
setLoadingLayers
73+
setLoadingLayers,
74+
pixelDistance
6075
);
6176

6277
mapRef.current.addLayer(mapLayers.current[key]);
@@ -70,6 +85,8 @@ export const loadLayer = (mapLayers, mapRef, mapContext, key, dataList, filtered
7085
}
7186
}
7287

88+
89+
7390
export const enableReferencedLayer = (referenceData, mapContext) => {
7491
// Do nothing if no reference data
7592
if (!referenceData) return;

0 commit comments

Comments
 (0)