import * as GIA from '@ntj/gaia';
const SLIDER_MIN = -48;
const SLIDER_MAX = 44;
const UTC_TO_JST = 9;
const REFRESH_INTERVAL_MS = 30 * 1000; // 30秒
const REFRESH_COUNT_MAX = 120; // 最大60分
const FILTER_COLOR = new GIA.value.Color(0, 0, 0, 0.4);
const DEFAULT_COLOR = new GIA.value.Color(0, 0, 0, 0);
const snowfallImage = '/asset/rainfallmesh/snow.png';
let sliderNum = 0;
let mapper: GIA.types.RainfallMeshMapper | undefined;
let intervalId: ReturnType<typeof setInterval> | undefined;
const updateCondition = (offset: number, withRainDrop: boolean) => {
map.setRainfallMeshCondition(
new GIA.value.RainfallMeshCondition({
offset,
withRainDrop,
snowfallImage,
}),
);
};
const targetToDate = (mmddhhmm: string): Date => {
const now = new Date();
const year = now.getFullYear();
const month = parseInt(mmddhhmm.slice(0, 2), 10) - 1;
const day = parseInt(mmddhhmm.slice(2, 4), 10);
const hour = parseInt(mmddhhmm.slice(4, 6), 10);
const minute = parseInt(mmddhhmm.slice(6, 8), 10);
const date = new Date(year, month, day, hour, minute);
date.setHours(date.getHours() + UTC_TO_JST);
return date;
};
// 右下凡例(強く推奨)
const legend = document.createElement('div');
Object.assign(legend.style, {
position: 'absolute',
right: '72px',
bottom: '40px',
width: '56px',
height: '200px',
padding: '4px',
background: 'rgba(255,255,255,0.8)',
borderRadius: '4px',
});
legend.innerHTML = '<b>降雨</b><br/>(mm/h)<br/><br/>100<br/>60<br/>40<br/>20<br/>10<br/>5<br/>3<br/>2<br/>1<br/><br/>❄ 降雪';
document.body.appendChild(legend);
// スライダと日時表示
const caption = document.createElement('div');
const slider = document.createElement('input');
slider.type = 'range';
slider.min = `${SLIDER_MIN}`;
slider.max = `${SLIDER_MAX}`;
slider.value = '0';
const rainDropCheck = document.createElement('input');
rainDropCheck.type = 'checkbox';
rainDropCheck.checked = true;
const renderCaption = () => {
const target = mapper?.get(sliderNum);
if (!target) {
caption.textContent = '準備中';
return;
}
const date = targetToDate(target);
const hh = String(date.getHours()).padStart(2, '0');
const mm = String(date.getMinutes()).padStart(2, '0');
caption.textContent = `${date.getDate()}日 ${hh}:${mm}`;
};
const onSliderTouched = () => {
sliderNum = parseInt(slider.value, 10);
updateCondition(sliderNum, rainDropCheck.checked);
renderCaption();
};
slider.addEventListener('mousemove', onSliderTouched);
slider.addEventListener('change', onSliderTouched);
rainDropCheck.addEventListener('change', () => updateCondition(sliderNum, rainDropCheck.checked));
// mapper 受信(offset→日時紐付け)
map.setRainfallMeshRefreshCallback((nextMapper) => {
mapper = nextMapper;
renderCaption();
});
// 表示開始
map.setLayerOrderBefore('colorFilter', 'rainfallMesh');
map.setColorFilterColor(FILTER_COLOR);
map.setTiltWithZoomOptions({
zoomRange: new GIA.value.ZoomRange(8, 14),
tiltRange: {min: 0, max: 45},
});
updateCondition(0, true);
// 定期再取得(強く推奨)
let count = 0;
intervalId = setInterval(() => {
map.refreshRainfallMesh();
count += 1;
if (count > REFRESH_COUNT_MAX && intervalId) {
clearInterval(intervalId);
intervalId = undefined;
}
}, REFRESH_INTERVAL_MS);
// 非表示時
const hide = () => {
map.setRainfallMeshCondition(undefined);
map.setTiltWithZoomOptions(undefined);
map.setColorFilterColor(DEFAULT_COLOR);
if (intervalId) {
clearInterval(intervalId);
intervalId = undefined;
}
legend.remove();
};
メッシュ版降雨条件を設定する
RainfallMeshConditionはoffset(-48〜44)時点の降雨・降雪メッシュを表示します。setRainfallMeshRefreshCallbackで受け取るmapperにはoffset -> target(mmddhhmm)の対応が入り、 これを使ってスライダ位置に対応した日時を表示できます。RainfallMeshConditionInitOptions
offset-48〜44)snowfallImagewithRainDroptrue)rainColorMapsnowColorMap