Le fichier bin du maillage de 1 km pluie analysée (2006-) fourni par l'Agence météorologique / Centre de soutien météorologique aux entreprises est au format GRIB2. Cependant, à partir d'avril 2020, il ne peut plus être géré par GDAL ou pygrib car il contient ses propres extensions. Il existe également une méthode de vidage binaire utilisant wgrib2 qui prend en charge l'analyse des précipitations ou la convertit en NetCDF et la lit, mais si vous souhaitez extraire uniquement la valeur numérique, il est plus pratique de la lire directement.
Les données se composent de ** 8 ** (sections 0 à 8, dont la section 2 est omise), dont la valeur de niveau de l'entier représentant les précipitations dans la section 7 est la longueur de la course comprimée à l'ouest. 2560 éléments sont stockés du nord à l'est [^ 1]. Les précipitations sont converties en convertissant cette valeur de niveau en fonction d'une table de recherche avec la ** valeur représentative des données ** (10 fois les précipitations représentant la grille de 1 km) définie dans la section 5, et en la multipliant par 1/10. Tu peux l'avoir.
Les spécifications des données binaires sont les Informations techniques sur les matériels de distribution (édition météorologique) n ° 238 et n ° 193 édition révisée Il est décrit dans /238.pdf). Les modèles utilisés dans chaque section sont spécifiés dans International Weather Report Ceremony / Separate Volume.
Chaque section a une section de longueur fixe et une section de longueur variable, et la longueur de section (nombre d'octets) et le numéro de section sont stockés au début de la section. Pour lire une section particulière, commencez la lecture en ajoutant les longueurs de la section précédente. Dans le format de données d'analyse des précipitations, les sections 0, 1, 3, 4 et 6 sont définies comme des longueurs fixes de 16, 21, 72, 82 et 6 octets dans l'ordre, et les sections 5 et 7 sont le nombre de niveaux et de précipitations. Il est variable selon la distribution. Cependant, dans la section 5, l'analyse actuelle des précipitations n'a pas changé à un maximum de 98 niveaux, elle est donc en fait de 213 octets (= 17 + 2 * 98).
Le tableau ci-dessous montre la correspondance entre les valeurs de niveau et les valeurs représentatives des données définies dans la section 5.
niveau | Valeur représentative des données | Précipitations correspondantes |
---|---|---|
1 | 0 | Pas de pluie, 0mm |
2 | 4 | Valeur minimale, 0.Moins de 5 mm |
3 | 10 | 1.0mm |
(...) | (En 10 incréments) | (Incréments de 1 mm) |
79 | 770 | 77.0mm |
80 | 800 | 80.0mm |
(...) | (Par 50 incréments) | (Par incréments de 5 mm) |
89 | 1250 | 125.0mm |
90 | 1300 | 130.0mm |
(...) | (Par 100 incréments) | (Par incréments de 10 mm) |
97 | 2000 | 200.0mm |
98 | 2550 | 205.0 mm ou plus |
En plus de cela, la section 7 inclut le niveau 0 comme «valeur manquante». Lors du décodage en tant que valeur entière, il est pratique d'attribuer une valeur appropriée, donc dans le code ci-dessous, elle est définie sur "-10".
La méthode de codage de longueur d'exécution utilisée dans la section 7 exprime le nombre de répétitions par la somme des nombres, et [Explication de la méthode de codage de longueur d'exécution](http://www.jmbsc.or.jp/jp/jp Développez en fonction de la formule (/online/joho-sample/Run-Length_Encoding.pdf).
Avant l'expansion, il s'agit d'une chaîne d'octets d'entiers non signés (0 à 255). La valeur de niveau de 0 à la valeur la plus élevée (<= 98) ou moins qui apparaît est la valeur de niveau telle quelle et la valeur de 0 à 255 correspondant au nombre de répétitions de la valeur de niveau précédent. Si ce dernier est continu, la somme des nombres ajoutés comme nombre de chiffres élevés dans l'ordre est le nombre de répétitions.
Voici la quantité de précipitations contenue dans l'échantillon du fichier d'analyse des précipitations téléchargé à partir du Meteorological Business Support Center. Code à lire comme ndarray de Numpy (Nord-Sud 3360 x Est-Ouest 2560).
from itertools import repeat
import struct
import numpy as np
def set_table(section5):
max_level = struct.unpack_from('>H', section5, 15)[0]
table = (
-10, # define representative of level 0 (Missing Value)
*struct.unpack_from('>'+str(max_level)+'H', section5, 18)
)
return np.array(table, dtype=np.int16)
def decode_runlength(code, hi_level):
for raw in code:
if raw <= hi_level:
level = raw
pwr = 0
yield level
else:
length = (0xFF - hi_level)**pwr * (raw - (hi_level + 1))
pwr += 1
yield from repeat(level, length)
def load_jmara_grib2(file):
with open(file, 'rb') as f:
binary = f.read()
len_ = {'sec0':16, 'sec1':21, 'sec3':72, 'sec4':82, 'sec6':6}
end4 = len_['sec0'] + len_['sec1'] + len_['sec3'] + len_['sec4'] - 1
len_['sec5'] = struct.unpack_from('>I', binary, end4+1)[0]
section5 = binary[end4:(end4+len_['sec5']+1)]
end6 = end4 + len_['sec5'] + len_['sec6']
len_['sec7'] = struct.unpack_from('>I', binary, end6+1)[0]
section7 = binary[end6:(end6+len_['sec7']+1)]
highest_level = struct.unpack_from('>H', section5, 13)[0]
level_table = set_table(section5)
decoded = np.fromiter(
decode_runlength(section7[6:], highest_level), dtype=np.int16
).reshape((3360, 2560))
# convert level to representative
return level_table[decoded]
file = rb'./SRF_GPV_Ggis1km_Prr60lv_ANAL_20180707/Z__C_RJTD_20180707000000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin'
rain = load_jmara_grib2(file) / 10
Si vous écrivez le code ci-dessus dans Fortran et en faites une bibliothèque, ce sera environ 40 fois plus rapide.
jmara.f90
subroutine load_jmara_grib2(filepath, decoded_2d)
implicit none
character(*), intent(in) :: filepath
integer, parameter :: len_sec0 = 16, len_sec1 = 21, len_sec2 = 0
integer, parameter :: len_sec3 = 72, len_sec4 = 82, len_sec6 = 6
integer :: len_sec5, len_sec7, pos, filesize
integer, parameter :: x_n = 2560, y_n = 3360
integer(1), allocatable :: binary(:)
integer(2), allocatable :: table(:)
integer(2) :: highest_level, decoded(x_n*y_n)
integer(2), intent(out) :: decoded_2d(x_n, y_n)
inquire(file=filepath, size=filesize)
allocate(binary(filesize))
open(100, file=trim(filepath), status='old', access='direct', form='unformatted', recl=filesize)
read(100, rec=1) binary
close(100)
pos = len_sec0 + len_sec1 + len_sec2 + len_sec3 + len_sec4
len_sec5 = pack_int32(binary(pos+1), binary(pos+2), binary(pos+3), binary(pos+4))
highest_level = pack_int16(binary(pos+13), binary(pos+14))
call set_table(binary(pos+1 : pos+len_sec5), table)
pos = pos + len_sec5 + len_sec6
len_sec7 = pack_int32(binary(pos+1), binary(pos+2), binary(pos+3), binary(pos+4))
call decode_runlength(binary(pos+6 : pos+len_sec7), highest_level, decoded)
where (decoded == 0)
decoded = -10 ! define representative of level 0 (Missing Value)
elsewhere
decoded = table(decoded)
endwhere
decoded_2d = reshape(decoded, (/ x_n, y_n /))
contains
subroutine set_table(section5, table)
implicit none
integer(1), intent(in) :: section5(:)
integer(2), allocatable, intent(out) :: table(:)
integer(2) :: max_level, level
max_level = pack_int16(section5(15), section5(16))
allocate(table(max_level))
do level = 1, max_level
table(level) = pack_int16(section5(16 + 2*level), section5(17 + 2*level))
end do
end subroutine
subroutine decode_runlength(code, hi_level, decoded)
implicit none
integer(1), intent(in) :: code(:)
integer(2), intent(in) :: hi_level
integer(2), intent(out) :: decoded(0:)
integer :: i, pwr, offset, length
integer(2) :: raw, level
offset = 0
do i = 1, size(code)
raw = get_int16(code(i))
if(raw <= hi_level) then
level = raw
pwr = 0
decoded(offset) = level
offset = offset + 1
else
length = (255 - hi_level)**pwr * (raw - (hi_level + 1))
pwr = pwr + 1
decoded(offset : offset+length-1) = level
offset = offset + length
end if
end do
end subroutine
integer(2) function pack_int16(arg1, arg2)
implicit none
integer(1) :: arg1, arg2
pack_int16 = ishft( iand(int(B'11111111',2), int(arg1, 2)), 8 ) &
+ iand(int(B'11111111',2), int(arg2, 2))
end function
integer(4) function pack_int32(arg1, arg2, arg3, arg4)
implicit none
integer(1) :: arg1, arg2, arg3, arg4
pack_int32 = ishft( iand(int(B'11111111',4), int(arg1, 4)), 8*3) &
+ ishft( iand(int(B'11111111',4), int(arg2, 4)), 8*2) &
+ ishft( iand(int(B'11111111',4), int(arg3, 4)), 8 ) &
+ iand(int(B'11111111',4), int(arg4, 4))
end function
integer(2) function get_int16(arg1)
implicit none
integer(1) :: arg1
get_int16 = iand(int(B'11111111',2), int(arg1, 2))
end function
end subroutine
jmara.Compiler en dll
gfortran -shared jmara.f90 -Ofast -march=native -fopt-info -o ./jmara.dll
Appel depuis Python avec ctypes
import ctypes
import numpy as np
def load_jmara_grib2(file):
lib = np.ctypeslib.load_library('jmara.dll', '.')
lib.load_jmara_grib2_.argtypes = [
ctypes.c_char_p,
np.ctypeslib.ndpointer(dtype=np.int16),
ctypes.c_size_t # hidden argument for character length
]
lib.load_jmara_grib2_.restype = ctypes.c_void_p
representative = np.zeros((3360, 2560), dtype=np.int16, order='C')
lib.load_jmara_grib2_(
ctypes.c_char_p(file),
representative,
ctypes.c_size_t(len(file))
)
return representative
file = rb'./SRF_GPV_Ggis1km_Prr60lv_ANAL_20180707/Z__C_RJTD_20180707000000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin'
rain = load_jmara_grib2(file) / 10
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 8))
cmap=plt.cm.jet
cmap.set_under('white')
plt.imshow(rain, aspect=8/12, vmin=0.4, cmap=cmap)
plt.colorbar()
plt.show()
Pour le tableau bidimensionnel «pluie» des précipitations obtenues, générez la latitude et la longitude et dessinez-les avec Matplotlib. Ici, considérant que les données sont une valeur représentative dans la grille, les coordonnées sont divisées en 80 parties égales de la grille de maillage cubique (80 parties égales de 1 degré dans le sens de la longitude et 40 minutes (= 1 / 1,5 degré) dans le sens de la latitude). ) Est défini pour être le centre (1/2).
conda install -c conda-forge basemap basemap-data-hires
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
lon = np.linspace(118, 150, 2560, endpoint=False) + 1/80 / 2
lat = np.linspace(48, 20, 3360, endpoint=False) - 1/80/1.5 / 2
X, Y = np.meshgrid(lon, lat)
fig, ax = plt.subplots(figsize=(10,8))
ax.set_title('JMA Radar/Raingauge-Analyzed Precipitation\n2018-07-07T00:00:00+00:00')
m = Basemap(llcrnrlat=20, urcrnrlat=48,
llcrnrlon=118, urcrnrlon=150,
resolution='l', ax=ax)
m.drawmeridians(np.arange(0, 360, 5), labels=[1, 0, 0, 1], linewidth=0)
m.drawparallels(np.arange(-90, 90, 5), labels=[1, 0, 1, 0], linewidth=0)
m.drawcoastlines(color='black')
levels = [0, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80]
colors = ['whitesmoke', 'gainsboro', 'darkgray', 'cyan', 'deepskyblue',
'blue', 'lime', 'yellow', 'orange', 'red']
im = m.contourf(X, Y, rain, levels, colors=colors, extend='both')
im.cmap.set_under('white')
im.cmap.set_over('magenta')
cb = m.colorbar(im, 'right', ticks=levels, size='2.5%')
cb.set_label('mm/hr')
plt.show()
Si vous développez un fichier, ce sera 65,6 Mo dans np.float64. Si vous renvoyez la valeur telle qu'elle est 10 fois, ce sera environ 16,4 Mo de np.int16.
Les supports de données achetés auprès du Meteorological Business Support Center sont inclus dans le disque annuel dans une hiérarchie telle que «DATA / aaaa / mm / jj». Si vous enregistrez maintenant cette année sous le nom rép_parent / aaaa / DATA / aaaa / mm / jj
, vous pouvez générer les chemins d'accès pour les fichiers bin individuels dans la période spécifiée comme suit:
import datetime
def half_hour_range(start, end):
for n in range(int((end - start).total_seconds() / 3600)):
yield start + datetime.timedelta(hours=n)
yield start + datetime.timedelta(hours=n, minutes=30)
def jmara_path(time, parent_dir='.'):
path = parent_dir.rstrip('/').rstrip('\\') + (
time.strftime('/%Y/DATA/%Y/%m/%d/')
+ 'Z__C_RJTD_'
+ time.strftime('%Y%m%d%H%M%S')
+ '_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin'
)
return path
start = datetime.datetime(2018, 7, 1, 0, 0, 0)
end = datetime.datetime(2018, 9, 1, 0, 0, 0)
parent_dir = 'G:'
filepaths = [jmara_path(t, parent_dir) for t in half_hour_range(start, end)]
filepaths
['G:/2018/DATA/2018/07/01/Z__C_RJTD_20180701000000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/07/01/Z__C_RJTD_20180701003000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/07/01/Z__C_RJTD_20180701010000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/07/01/Z__C_RJTD_20180701013000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/07/01/Z__C_RJTD_20180701020000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
(...)
'G:/2018/DATA/2018/08/31/Z__C_RJTD_20180831213000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/08/31/Z__C_RJTD_20180831220000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/08/31/Z__C_RJTD_20180831223000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/08/31/Z__C_RJTD_20180831230000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin',
'G:/2018/DATA/2018/08/31/Z__C_RJTD_20180831233000_SRF_GPV_Ggis1km_Prr60lv_ANAL_grib2.bin']
[^ 1]: Dans le 72e octet de la section 4, 0x00
est spécifié comme mode de balayage, qui est` les premières données à l'extrémité nord-ouest de la plage, empruntant la représentation de format.txt attachée à l'analyse des précipitations. Oui, si vous vous alignez d'ouest en est à la même latitude et que vous avancez du nombre de points de grille le long de la ligne de latitude, vous vous déplacerez vers les données à l'extrémité ouest d'une grille au sud, et les données à la même latitude seront à nouveau alignées par le nombre de points de grille le long de la ligne de latitude.
Recommended Posts