import React, { useEffect, useContext, useState, useCallback, useRef, } from "react"; import { Map, TileLayer, AttributionControl, Marker } from "react-leaflet"; import PropTypes from "prop-types"; import { AppContext } from "~/AppContext"; import debounce from "debounce"; import axios from "axios"; import styles from "./styles.css"; /** * Weather map * * @param {Object} props * @param {Number} props.zoom zoom level * @param {Boolean} [props.dark] dark mode * @returns {JSX.Element} Weather map */ const WeatherMap = ({ zoom, dark }) => { const MAP_CLICK_DEBOUNCE_TIME = 200; //ms const { setMapPosition, panToCoords, setPanToCoords, browserGeo, mapGeo, mapApiKey, getMapApiKey, markerIsVisible, animateWeatherMap, } = useContext(AppContext); const mapRef = useRef(); const mapClickHandler = useCallback( debounce((e) => { const { lat: latitude, lng: longitude } = e.latlng; const newCoords = { latitude, longitude }; setMapPosition(newCoords); }, MAP_CLICK_DEBOUNCE_TIME), [setMapPosition] ); const [mapTimestamps, setMapTimestamps] = useState(null); const [mapTimestamp, setMapTimestamp] = useState(null); const [currentMapTimestampIdx, setCurrentMapTimestampIdx] = useState(0); const MAP_TIMESTAMP_REFRESH_FREQUENCY = 1000 * 60 * 10; //update every 10 minutes const MAP_CYCLE_RATE = 500; //ms const getMapApiKeyCallback = useCallback(() => getMapApiKey(), [ getMapApiKey, ]); //const delay = ms => new Promise(res => setTimeout(res, ms)); useEffect(() => { getMapApiKeyCallback().catch((err) => { console.log("err!", err); }); const updateTimeStamps = () => { getMapTimestamps() .then((res) => { setMapTimestamps(res); }) .catch((err) => { console.log("err", err); }); }; const mapTimestampsInterval = setInterval( updateTimeStamps, MAP_TIMESTAMP_REFRESH_FREQUENCY ); updateTimeStamps(); //initial update return () => { clearInterval(mapTimestampsInterval); }; }, []); // eslint-disable-line react-hooks/exhaustive-deps // Pan the screen to a a specific location when `panToCoords` is updated with grid coordinates useEffect(() => { if (panToCoords && mapRef.current) { const { leafletElement } = mapRef.current; leafletElement.panTo([panToCoords.latitude, panToCoords.longitude]); setPanToCoords(null); //reset back to null so we can observe a change next time its fired for the same coords } }, [panToCoords, mapRef]); // eslint-disable-line react-hooks/exhaustive-deps const { latitude, longitude } = browserGeo || {}; useEffect(() => { if (mapTimestamps) { setMapTimestamp(mapTimestamps[currentMapTimestampIdx]); } }, [currentMapTimestampIdx, mapTimestamps]); // cycle through weather maps when animated is enabled useEffect(() => { if (mapTimestamps) { if (animateWeatherMap) { let cycleRate = MAP_CYCLE_RATE; const interval = setInterval(() => { let nextIdx; //let d = new Date(mapTimestamps[currentMapTimestampIdx]*1000); //console.log(d.toTimeString()); if (currentMapTimestampIdx + 1 >= mapTimestamps.length) { nextIdx = 0; cycleRate = 5000; } else { nextIdx = currentMapTimestampIdx + 1; } setCurrentMapTimestampIdx(nextIdx); }, cycleRate); return () => { //console.log(cycleRate); clearInterval(interval); }; } else { setCurrentMapTimestampIdx(mapTimestamps.length - 1); } } }, [currentMapTimestampIdx, animateWeatherMap, mapTimestamps]); if (!hasVal(latitude) || !hasVal(longitude) || !zoom || !mapApiKey) { return (
Cannot retrieve map data.
Did you enter an API key?
); } const markerPosition = mapGeo ? [mapGeo.latitude, mapGeo.longitude] : null; return ( {mapTimestamp ? ( ) : null} {markerIsVisible && markerPosition ? ( ) : null} ); }; WeatherMap.propTypes = { zoom: PropTypes.number.isRequired, dark: PropTypes.bool, }; /** * Weather layer * * @param {Object} props * @param {String} props.layer * @param {String} props.weatherApiKey * @returns {JSX.Element} Weather layer */ const WeatherLayer = ({ layer, weatherApiKey }) => { return ( ); }; WeatherLayer.propTypes = { layer: PropTypes.string.isRequired, weatherApiKey: PropTypes.string, }; /** * Determines if truthy, but returns true for 0 * * @param {*} i * @returns {Boolean} If truthy or zero */ function hasVal(i) { return !!(i || i === 0); } /** * Get timestamps for weather map * * @returns {Promise} Promise of timestamps */ function getMapTimestamps() { return new Promise((resolve, reject) => { axios .get("https://api.rainviewer.com/public/maps.json") .then((res) => { resolve(res.data); }) .catch((err) => { reject(err); }); }); } export default WeatherMap;