Skip to content

Commit be8e659

Browse files
authored
Merge pull request #62 from UTSC-CSCC01-Software-Engineering-I/updates
added more filtering options
2 parents 8855ec8 + c3669b7 commit be8e659

File tree

3 files changed

+199
-23
lines changed

3 files changed

+199
-23
lines changed

frontend/src/components/HUDleftPoints.jsx

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ function LogoBlock() {
2626
const [showSortMenu, setShowSortMenu] = useState(false);
2727
const [unit, setUnit] = useState('C');
2828
const [showFilterModal, setShowFilterModal] = useState(false);
29-
const [tempFilter, setTempFilter] = useState({ min: '', max: '' });
29+
const [tempFilter, setTempFilter] = useState({
30+
min: '', max: '',
31+
distanceMin: '', distanceMax: '',
32+
maxAge: ''
33+
});
3034

3135
// Add new state for user location
3236
const [userLocation, setUserLocation] = useState(null);
@@ -303,39 +307,66 @@ function LogoBlock() {
303307

304308
// NEW: dispatch filterchange event
305309
const handleApplyFilter = () => {
306-
const min = parseFloat(tempFilter.min);
307-
const max = parseFloat(tempFilter.max);
310+
const min = parseFloat(tempFilter.min);
311+
const max = parseFloat(tempFilter.max);
312+
const distanceMax = parseFloat(tempFilter.distanceMax);
313+
const maxAge = parseFloat(tempFilter.maxAge);
308314
window.dispatchEvent(new CustomEvent('filterchange', {
309-
detail: { min: isNaN(min) ? NaN : min, max: isNaN(max) ? NaN : max }
315+
detail: {
316+
min: isNaN(min) ? NaN : min,
317+
max: isNaN(max) ? NaN : max,
318+
distanceMax:isNaN(distanceMax)? NaN : distanceMax,
319+
maxAge: isNaN(maxAge) ? NaN : maxAge
320+
}
310321
}));
311322
setShowFilterModal(false);
312323
};
313324

314325
// NEW: reset filter AND dispatch
315326
const handleResetFilter = () => {
316-
setTempFilter({ min: '', max: '' });
327+
setTempFilter({ min:'', max:'', distanceMax:'', maxAge:'' });
317328
window.dispatchEvent(new CustomEvent('filterchange', {
318-
detail: { min: NaN, max: NaN }
329+
detail: {
330+
min: NaN, max: NaN,
331+
distanceMax: NaN,
332+
maxAge: NaN
333+
}
319334
}));
320335
setShowFilterModal(false);
321336
};
322337

323338
// Listen for filterchange events and update the side‐panel list
324339
useEffect(() => {
325340
function handleFilterChange(e) {
326-
const { min, max } = e.detail;
327-
setFilteredList(
328-
locaList.filter(item =>
329-
(isNaN(min) || item.temp >= min) &&
330-
(isNaN(max) || item.temp <= max)
331-
)
332-
);
341+
const { min, max, distanceMax, maxAge } = e.detail;
342+
const now = Date.now();
343+
setFilteredList(locaList.filter(item => {
344+
const tempOK = (isNaN(min) || item.temp >= min) &&
345+
(isNaN(max) || item.temp <= max);
346+
347+
let distOK = true;
348+
if (!isNaN(distanceMax) && userLocation) {
349+
const d = calculateDistance(
350+
userLocation.latitude,
351+
userLocation.longitude,
352+
item.lat, item.lng || item.lon
353+
);
354+
if (d > distanceMax) distOK = false;
355+
}
356+
357+
let ageOK = true;
358+
if (!isNaN(maxAge) && item.timestamp) {
359+
const ageDays = (now - new Date(item.timestamp).getTime())
360+
/ (1000*60*60*24);
361+
if (ageDays > maxAge) ageOK = false;
362+
}
363+
364+
return tempOK && distOK && ageOK;
365+
}));
333366
}
334367
window.addEventListener('filterchange', handleFilterChange);
335-
return () => {
336-
window.removeEventListener('filterchange', handleFilterChange);
337-
};
338-
}, [locaList]);
368+
return () => window.removeEventListener('filterchange', handleFilterChange);
369+
}, [locaList, userLocation]);
339370

340371
return (
341372
<div

frontend/src/components/MapComponent.jsx

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export default function MapComponent() {
6363
const mapInstanceRef = useRef(null);
6464
const markersRef = useRef([]);
6565
const [unit, setUnit] = useState(() => UnitManager.getUnit());
66+
const userLocationRef = useRef(null);
6667

6768
// Temperature color function - works with both Celsius and Fahrenheit
6869
function getTemperatureColor(temp, unit = 'C', mode = 'light') {
@@ -724,7 +725,7 @@ export default function MapComponent() {
724725
}, 100);
725726
});
726727

727-
markersRef.current.push({ marker, tempC: t, name, lat, lon });
728+
markersRef.current.push({ marker, tempC: t, name, lat, lon, timestamp: ts });
728729
}
729730
}
730731

@@ -1122,6 +1123,79 @@ export default function MapComponent() {
11221123
};
11231124
}, []);
11241125

1126+
// new single useEffect to handle ALL filters
1127+
useEffect(() => {
1128+
function calculateDistance(lat1, lon1, lat2, lon2) {
1129+
const R = 6371;
1130+
const dLat = (lat2 - lat1)*Math.PI/180;
1131+
const dLon = (lon2 - lon1)*Math.PI/180;
1132+
const a = Math.sin(dLat/2)**2 +
1133+
Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180) *
1134+
Math.sin(dLon/2)**2;
1135+
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
1136+
}
1137+
1138+
const handleFilter = e => {
1139+
const { min, max, distanceMax, maxAge } = e.detail;
1140+
const now = Date.now();
1141+
1142+
// If distance filter is requested but no location, try to get it
1143+
if (!isNaN(distanceMax) && !userLocationRef.current) {
1144+
console.log('🔄 Distance filter requested, attempting to get location...');
1145+
if (navigator.geolocation) {
1146+
navigator.geolocation.getCurrentPosition(
1147+
(position) => {
1148+
userLocationRef.current = {
1149+
lat: position.coords.latitude,
1150+
lon: position.coords.longitude
1151+
};
1152+
console.log('📍 Got user location for filtering:', userLocationRef.current);
1153+
// Re-trigger the filter with location now available
1154+
handleFilter(e);
1155+
},
1156+
(error) => {
1157+
console.warn('❌ Could not get location for distance filter:', error.message);
1158+
alert('Location access is required for distance filtering. Please enable location permissions and try again.');
1159+
}
1160+
);
1161+
return; // Exit early, will re-run once location is obtained
1162+
} else {
1163+
alert('Geolocation is not supported by this browser.');
1164+
return;
1165+
}
1166+
}
1167+
1168+
markersRef.current.forEach(({ marker, tempC, lat, lon, timestamp }) => {
1169+
let keep = true;
1170+
if (!isNaN(min) && tempC < min) keep = false;
1171+
if (!isNaN(max) && tempC > max) keep = false;
1172+
1173+
if (!isNaN(distanceMax) && userLocationRef.current) {
1174+
const d = calculateDistance(
1175+
userLocationRef.current.lat,
1176+
userLocationRef.current.lon,
1177+
lat, lon
1178+
);
1179+
if (d > distanceMax) keep = false;
1180+
}
1181+
1182+
if (!isNaN(maxAge) && timestamp) {
1183+
const ageDays = (now - timestamp)/(1000*60*60*24);
1184+
if (ageDays > maxAge) keep = false;
1185+
}
1186+
1187+
if (keep) {
1188+
marker.addTo(mapInstanceRef.current);
1189+
} else {
1190+
mapInstanceRef.current.removeLayer(marker);
1191+
}
1192+
});
1193+
};
1194+
1195+
window.addEventListener('filterchange', handleFilter);
1196+
return () => window.removeEventListener('filterchange', handleFilter);
1197+
}, []);
1198+
11251199
return (
11261200
<div style={{ position: 'relative', height: '100vh', width: '100%' }}>
11271201
{/* Map container */}

frontend/src/components/TempFilterModal.jsx

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { createPortal } from 'react-dom';
33

4-
function TempFilterModal({
4+
export default function TempFilterModal({
55
show,
66
onClose,
77
theme,
@@ -80,7 +80,7 @@ function TempFilterModal({
8080
<div style={{
8181
display: 'flex',
8282
gap: '1rem',
83-
marginBottom: '2rem'
83+
marginBottom: '1rem'
8484
}}>
8585
<div style={{ flex: 1 }}>
8686
<label htmlFor="temp-min" style={{
@@ -156,6 +156,79 @@ function TempFilterModal({
156156
/>
157157
</div>
158158
</div>
159+
160+
{/* only Max Distance now */}
161+
<div style={{ marginBottom: '1rem' }}>
162+
<label htmlFor="dist-max" style={{
163+
display: 'block',
164+
fontSize: '0.85rem',
165+
fontWeight: '500',
166+
marginBottom: '0.5rem',
167+
color: theme === 'light' ? 'rgba(0,0,0,0.8)' : 'rgba(255,255,255,0.8)'
168+
}}>
169+
Max Distance (km)
170+
</label>
171+
<input
172+
type="number"
173+
id="dist-max"
174+
placeholder="e.g. 10"
175+
value={tempFilter.distanceMax}
176+
onChange={e => setTempFilter(f => ({ ...f, distanceMax: e.target.value }))}
177+
style={{
178+
width: '100%',
179+
padding: '0.5rem',
180+
borderRadius: '0.5rem',
181+
border: theme === 'light'
182+
? '1px solid rgba(0,0,0,0.1)'
183+
: '1px solid rgba(255,255,255,0.2)',
184+
backgroundColor: theme === 'light' ? '#fff' : 'rgba(255,255,255,0.1)',
185+
color: theme === 'light' ? '#000' : '#fff',
186+
fontSize: '1rem',
187+
outline: 'none',
188+
transition: 'border-color 0.2s ease',
189+
boxSizing: 'border-box'
190+
}}
191+
onFocus={e => e.target.style.borderColor = theme === 'light' ? '#007AFF' : '#0A84FF'}
192+
onBlur={e => e.target.style.borderColor = theme === 'light' ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.2)'}
193+
/>
194+
</div>
195+
196+
<div style={{ marginBottom: '1rem' }}>
197+
<label htmlFor="max-age" style={{
198+
display: 'block',
199+
fontSize: '0.85rem',
200+
fontWeight: '500',
201+
marginBottom: '0.5rem',
202+
color: theme === 'light' ? 'rgba(0,0,0,0.8)' : 'rgba(255,255,255,0.8)'
203+
}}>
204+
Max Age (days)
205+
</label>
206+
<input
207+
type="number"
208+
id="max-age"
209+
placeholder="7"
210+
value={tempFilter.maxAge}
211+
onChange={e => setTempFilter(f => ({ ...f, maxAge: e.target.value }))}
212+
style={{
213+
width: '100%',
214+
padding: '0.5rem',
215+
borderRadius: '0.5rem',
216+
border: theme === 'light'
217+
? '1px solid rgba(0,0,0,0.1)'
218+
: '1px solid rgba(255,255,255,0.2)',
219+
backgroundColor: theme === 'light'
220+
? '#fff'
221+
: 'rgba(255,255,255,0.1)',
222+
color: theme === 'light' ? '#000' : '#fff',
223+
fontSize: '1rem',
224+
outline: 'none',
225+
transition: 'border-color 0.2s ease',
226+
boxSizing: 'border-box'
227+
}}
228+
onFocus={e => e.target.style.borderColor = theme === 'light' ? '#007AFF' : '#0A84FF'}
229+
onBlur={e => e.target.style.borderColor = theme === 'light' ? 'rgba(0,0,0,0.1)' : 'rgba(255,255,255,0.2)'}
230+
/>
231+
</div>
159232

160233
<div style={{
161234
display: 'flex',
@@ -240,6 +313,4 @@ function TempFilterModal({
240313
</div>,
241314
document.body
242315
) : null;
243-
}
244-
245-
export default TempFilterModal;
316+
}

0 commit comments

Comments
 (0)