Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 31 additions & 68 deletions src/components/MapSection.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
'use client';

import React, { useMemo, useRef, useState, useEffect, useCallback } from 'react';
import Map, { NavigationControl, Source, Layer, type MapRef } from 'react-map-gl/maplibre';
import React, { useMemo, useState, useEffect } from 'react';
import Map, { Marker } from 'react-map-gl/maplibre';
import 'maplibre-gl/dist/maplibre-gl.css';
import { fetchAndProcessStationData, type StationGeoJSON } from '@/lib/rts';
import { fetchAndProcessStationData, type StationGeoJSON, type StationFeature } from '@/lib/rts';
import StationMarker from './StationMarker';

const MapSection = React.memo(() => {
const mapRef = useRef<MapRef>(null);
const [stationData, setStationData] = useState<StationGeoJSON | null>(null);
const [dataTime, setDataTime] = useState<number>(0);
const [maxIntensity, setMaxIntensity] = useState<number>(-3);
const [isMapReady, setIsMapReady] = useState<boolean>(false);
const sourceInitializedRef = useRef<boolean>(false);

const mapStyle: any = useMemo(() => ({
version: 8,
Expand Down Expand Up @@ -98,52 +96,6 @@ const MapSection = React.memo(() => {
return `${year}/${month}/${day} ${hours}:${minutes}:${seconds}`;
};

const initializeMapSource = useCallback(() => {
if (!mapRef.current || !stationData || sourceInitializedRef.current) return;

const map = mapRef.current.getMap();

if (!map.getSource('stations')) {
map.addSource('stations', {
type: 'geojson',
data: stationData,
});

map.addLayer({
id: 'station-circles',
type: 'circle',
source: 'stations',
layout: {
'circle-sort-key': ['get', 'sortKey'],
},
paint: {
'circle-radius': 4,
'circle-color': ['get', 'color'],
'circle-opacity': 1,
'circle-stroke-width': 1,
'circle-stroke-color': '#ffffff',
},
});

sourceInitializedRef.current = true;
}
}, [stationData]);

const updateMapData = useCallback((newData: StationGeoJSON) => {
if (!mapRef.current || !sourceInitializedRef.current) return;

const map = mapRef.current.getMap();
const source = map.getSource('stations') as any;

if (source && source.setData) {
source.setData(newData);
}
}, []);

const handleMapLoad = useCallback(() => {
setIsMapReady(true);
}, []);

useEffect(() => {
const fetchData = async () => {
try {
Expand All @@ -153,36 +105,47 @@ const MapSection = React.memo(() => {
setDataTime(data.time);

let max = -3;
data.geojson.features.forEach(feature => {
if (feature.properties.intensity > max) {
max = feature.properties.intensity;
}
});
if (data.geojson && data.geojson.features) {
data.geojson.features.forEach(feature => {
if (feature.properties.intensity > max) {
max = feature.properties.intensity;
}
});
}
setMaxIntensity(max);

if (isMapReady && sourceInitializedRef.current) {
updateMapData(data.geojson);
}
} catch (error) {
console.error("Failed to fetch station data:", error);
}
};

fetchData();
const interval = setInterval(fetchData, 1000);

return () => clearInterval(interval);
}, [isMapReady, updateMapData]);
}, []);

useEffect(() => {
if (isMapReady && stationData) {
initializeMapSource();
}
}, [isMapReady, stationData, initializeMapSource]);
const stationMarkers = useMemo(() => {
if (!stationData) return null;
return stationData.features.map((station: StationFeature) => (
<Marker
key={station.properties.id}
longitude={station.geometry.coordinates[0]}
latitude={station.geometry.coordinates[1]}
anchor="center"
>
<StationMarker
intensity={station.properties.intensity}
color={station.properties.color}
alert={station.properties.alert}
/>
</Marker>
));
}, [stationData]);

return (
<div className="w-1/2 h-full relative">
<Map
ref={mapRef}
initialViewState={{
longitude: 120.8,
latitude: 23.6,
Expand All @@ -198,9 +161,9 @@ const MapSection = React.memo(() => {
dragRotate={false}
touchZoomRotate={false}
boxZoom={false}
onLoad={handleMapLoad}
onError={() => {}}
>
{stationMarkers}
</Map>
{dataTime > 0 && (
<div className="absolute bottom-3 right-3 z-50 flex flex-col gap-2 items-end">
Expand Down
88 changes: 88 additions & 0 deletions src/components/StationMarker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from 'react';

interface StationMarkerProps {
intensity: number;
color: string;
alert?: boolean;
}

const StationMarker: React.FC<StationMarkerProps> = ({ intensity, color, alert }) => {
const size = 23;

if (!alert || intensity < 0.5) {
return (
<div
style={{
width: `${size / 2}px`,
height: `${size / 2}px`,
backgroundColor: color,
borderRadius: '50%',
border: '1px solid #ffffff',
zIndex: 1,
}}
/>
);
}

let bgColor = '';
let textColor = '';
let text = '';

if (intensity >= 0.5 && intensity <= 1.4) {
bgColor = '#003264';
textColor = '#ffffff';
text = '1';
} else if (intensity >= 1.5 && intensity <= 2.4) {
bgColor = '#0064c8';
textColor = '#ffffff';
text = '2';
} else if (intensity >= 2.5 && intensity <= 3.4) {
bgColor = '#1e9632';
textColor = '#ffffff';
text = '3';
} else if (intensity >= 3.5 && intensity <= 4.4) {
bgColor = '#ffc800';
textColor = '#000000';
text = '4';
} else if (intensity >= 4.5 && intensity <= 4.9) {
bgColor = '#ff9600';
textColor = '#000000';
text = '5⁻';
} else if (intensity >= 5.0 && intensity <= 5.4) {
bgColor = '#ff6400';
textColor = '#000000';
text = '5⁺';
} else if (intensity >= 5.5 && intensity <= 5.9) {
bgColor = '#ff0000';
textColor = '#ffffff';
text = '6⁻';
} else if (intensity >= 6.0 && intensity <= 6.4) {
bgColor = '#c00000';
textColor = '#ffffff';
text = '6⁺';
} else if (intensity >= 6.5) {
bgColor = '#9600c8';
textColor = '#ffffff';
text = '7';
}

return (
<div
className="flex items-center justify-center font-semibold"
style={{
width: `${size}px`,
height: `${size}px`,
backgroundColor: bgColor,
color: textColor,
borderRadius: '50%',
border: `1px solid ${textColor}`,
fontSize: '12px',
zIndex: Math.floor(intensity) + 10,
}}
>
{text}
</div>
);
};

export default StationMarker;
2 changes: 2 additions & 0 deletions src/lib/rts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface StationFeature {
intensity: number;
color: string;
sortKey: number;
alert?: boolean;
};
}

Expand Down Expand Up @@ -171,6 +172,7 @@ export function createStationGeoJSON(
intensity,
color,
sortKey: intensity,
alert: !!rts.alert,
},
});
}
Expand Down