Skip to content

Commit e6af903

Browse files
Merge pull request #25 from venisha-kalola/map-space
Map space
2 parents 58acabb + 670a7dd commit e6af903

File tree

3 files changed

+88
-4
lines changed

3 files changed

+88
-4
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
"dependencies": {
1212
"react": "^18.3.1",
1313
"react-dom": "^18.3.1",
14-
"react-router-dom": "^6.27.0"
14+
"react-router-dom": "^6.27.0",
15+
"leaflet": "^1.9.4",
16+
"react-leaflet": "^4.2.1"
1517
},
1618
"devDependencies": {
1719
"@vitejs/plugin-react": "^4.3.1",

src/components/IssMap.jsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet';
2+
import L from 'leaflet';
3+
import { useEffect, useRef } from 'react';
4+
import 'leaflet/dist/leaflet.css';
5+
6+
// Default Leaflet icon fix (since CRA/Vite bundling sometimes needs explicit paths)
7+
const icon = new L.Icon({
8+
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
9+
iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
10+
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
11+
iconSize: [25, 41],
12+
iconAnchor: [12, 41],
13+
popupAnchor: [1, -34],
14+
shadowSize: [41, 41]
15+
});
16+
17+
/**
18+
* IssMap component
19+
* Props:
20+
* - latitude (string or number)
21+
* - longitude (string or number)
22+
*/
23+
export default function IssMap({ latitude, longitude }) {
24+
const lat = parseFloat(latitude);
25+
const lon = parseFloat(longitude);
26+
const mapRef = useRef(null);
27+
28+
useEffect(() => {
29+
if (mapRef.current) {
30+
mapRef.current.setView([lat, lon]);
31+
}
32+
}, [lat, lon]);
33+
34+
if (Number.isNaN(lat) || Number.isNaN(lon)) return null;
35+
36+
return (
37+
<div style={{ height: '320px', width: '100%', borderRadius: 8, overflow: 'hidden' }}>
38+
<MapContainer
39+
center={[lat, lon]}
40+
zoom={3}
41+
ref={mapRef}
42+
style={{ height: '100%', width: '100%' }}
43+
scrollWheelZoom={false}
44+
>
45+
<TileLayer
46+
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
47+
url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
48+
/>
49+
<Marker position={[lat, lon]} icon={icon}>
50+
<Popup>
51+
ISS Position<br />Lat: {lat.toFixed(2)} Lon: {lon.toFixed(2)}
52+
</Popup>
53+
</Marker>
54+
</MapContainer>
55+
</div>
56+
);
57+
}

src/pages/Space.jsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,28 @@
1717
* - [ ] Track path trail (polyline) on map over session
1818
* - [ ] Extract map component & custom hook (useIssPosition)
1919
*/
20-
import { useEffect, useState } from 'react';
20+
import { useEffect, useState, useRef } from 'react';
2121
import Loading from '../components/Loading.jsx';
2222
import ErrorMessage from '../components/ErrorMessage.jsx';
2323
import Card from '../components/Card.jsx';
24+
import IssMap from '../components/IssMap.jsx';
2425

2526
export default function Space() {
2627
const [iss, setIss] = useState(null);
2728
const [crew, setCrew] = useState([]);
2829
const [error, setError] = useState(null);
2930
const [loading, setLoading] = useState(false);
31+
const [lastUpdated, setLastUpdated] = useState(null);
32+
const intervalRef = useRef(null);
3033

31-
useEffect(() => { fetchData(); }, []);
34+
useEffect(() => {
35+
fetchData();
36+
// Poll every 5s for updated ISS position only
37+
intervalRef.current = setInterval(() => {
38+
refreshIssOnly();
39+
}, 5000);
40+
return () => { if (intervalRef.current) clearInterval(intervalRef.current); };
41+
}, []);
3242

3343
async function fetchData() {
3444
try {
@@ -42,9 +52,23 @@ export default function Space() {
4252
const crewJson = await crewRes.json();
4353
setIss(issJson);
4454
setCrew(crewJson.people || []);
55+
setLastUpdated(new Date());
4556
} catch (e) { setError(e); } finally { setLoading(false); }
4657
}
4758

59+
async function refreshIssOnly() {
60+
try {
61+
const res = await fetch('http://api.open-notify.org/iss-now.json');
62+
if (!res.ok) throw new Error('Failed to refresh ISS');
63+
const issJson = await res.json();
64+
setIss(issJson);
65+
setLastUpdated(new Date());
66+
} catch (e) {
67+
// don't clobber existing data, but surface error
68+
setError(e);
69+
}
70+
}
71+
//leaflet map component
4872
return (
4973
<div>
5074
<h2>Space & Astronomy</h2>
@@ -54,7 +78,8 @@ export default function Space() {
5478
<Card title="ISS Current Location">
5579
<p>Latitude: {iss.iss_position.latitude}</p>
5680
<p>Longitude: {iss.iss_position.longitude}</p>
57-
{/* TODO: Render map (Leaflet) with marker for ISS position */}
81+
{lastUpdated && <p style={{ fontSize: '0.8rem', color: '#666' }}>Last updated: {lastUpdated.toLocaleTimeString()}</p>}
82+
<IssMap latitude={iss.iss_position.latitude} longitude={iss.iss_position.longitude} />
5883
</Card>
5984
)}
6085
<Card title={`Astronauts in Space (${crew.length})`}>

0 commit comments

Comments
 (0)