-
Notifications
You must be signed in to change notification settings - Fork 7
/
20190629.import_tropical_cyclone_images.js
1936 lines (1698 loc) · 64.4 KB
/
20190629.import_tropical_cyclone_images.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// cd /d D:\USB\cgi-bin\program\wiki && node 20190629.import_tropical_cyclone_images.js PAGASA
/*
2019/7/2 17:17:45 初版試營運 熱帶氣旋/颱風預測路徑圖的分類 modify from 20181016.import_earthquake_shakemap.js
2019/7/4 22:17:53 Import 交通部中央氣象局 typhoon forecast maps 路徑潛勢預報 https://www.cwb.gov.tw/V8/C/P/Typhoon/TY_NEWS.html
2019/7/5 6:23:58 Import Joint Typhoon Warning Center (JTWC)'s Tropical Warnings map https://www.metoc.navy.mil/jtwc/jtwc.html
2019/7/22 16:1:0 Import JMA typhoon forecast maps
2019/7/26 20:49:2 盡量統一檔案名稱。檔名不添加氣旋名稱,以統一氣旋存活各時期的檔案名稱。CWB, JMA 在颱風命名後無法取得命名前之編號,因此颱風命名後會採用另一個檔案名稱。現在應該只會在颱風命名前後變更一次。
2019/8/4 19:7:13 Import PAGASA typhoon forecast maps http://bagong.pagasa.dost.gov.ph/tropical-cyclone/severe-weather-bulletin
TODO:
https://www.nhc.noaa.gov/archive/2019/ONE-E_graphics.php?product=5day_cone_with_line_and_wind
[[Category:375m-resolution VIIRS images of tropical cyclones]]
*/
// ----------------------------------------------------------------------------
'use strict';
// Load CeJS library and modules.
require('../wiki loader.js');
CeL.get_URL.default_user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4710.4 Safari/537.36';
login_options.configuration_adapter = adapt_configuration;
var data_directory = base_directory + 'data/',
/** {Boolean}若在 media_directory 目錄下已有 cache 檔案就不再 upload。 */
skip_cached = false, media_directory = base_directory + 'media/',
/** {Object}wiki operator 操作子. */
wiki = Wiki(true, 'commons' /* && 'test' */),
//
cache_filename_label = '%4Y-%2m-%2d',
// 因為每個風暴會持續好幾天,甚至跨月,因此只標注年份。
filename_prefix = '%4Y ';
// ----------------------------------------------------------------------------
/**
* 由設定頁面讀入手動設定 manual settings。
*
* @param {Object}latest_task_configuration
* 最新的任務設定。
*/
function adapt_configuration(latest_task_configuration) {
// console.log(latest_task_configuration);
// console.log(wiki);
// 一般設定
var general = latest_task_configuration.general
|| (latest_task_configuration.general = Object.create(null));
if (!general) {
// CeL.info('No configuration.');
}
// 衛星圖像優先度 https://www.nrlmry.navy.mil/tcdat/tc2021/
var satellite_image_priority = latest_task_configuration['NRL satellite image priority'];
if (CeL.is_Object(satellite_image_priority)) {
for ( var area_code in satellite_image_priority) {
satellite_image_priority[area_code] = satellite_image_priority[area_code]
//
.split(',').map(function(satellite) {
return satellite.trim();
}).filter(function(satellite) {
return !!satellite;
});
}
if (false) {
console
.log(wiki.latest_task_configuration['NRL satellite image priority']);
}
}
}
// 先創建出/準備好本任務獨有的目錄,以便後續將所有的衍生檔案,如記錄檔、cache 等置放此目錄下。
if (data_directory || media_directory) {
prepare_directory(base_directory);
data_directory && prepare_directory(data_directory);
media_directory && prepare_directory(media_directory);
}
// https://stackoverflow.com/questions/20082893/unable-to-verify-leaf-signature
// for NRL Error: unable to verify the first certificate
// code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
// Or set https.request({... rejectUnauthorized:false })
// https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/250697/
// 2021/8/3 6:18:27 Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not
// match certificate's altnames: Host: www.metoc.navy.mil. is not in the cert's
// altnames: ...
function area_is_Southern_Hemisphere(area) {
return /South|Australian|India/i.test(area) && !/North Indian/i.test(area);
}
function get_year_range(is_Southern_Hemisphere, date) {
if (!date)
date = new Date;
if (typeof is_Southern_Hemisphere === 'string')
is_Southern_Hemisphere = area_is_Southern_Hemisphere(is_Southern_Hemisphere);
var year_range = date.getUTCFullYear();
if (is_Southern_Hemisphere) {
// 由公元7月1日至翌年6月31日,UTC
if (date.getUTCMonth() < 7 - 1) {
// 從前1年算起。
year_range--;
}
year_range = String(year_range) + '-' + ((year_range + 1) % 100);
} else {
year_range = String(year_range);
}
return year_range;
}
// category_to_parent_hash[category_name] = parent_category_name
// category_to_parent_hash['Category:2019 Pacific typhoon season track maps'] =
// 'Category:2019 Pacific typhoon season'
var category_to_parent_hash = Object.create(null);
[
'Pacific hurricane season',
'Pacific typhoon season',
// Category:Tropical cyclones by season
'Atlantic hurricane season',
'North Indian Ocean cyclone season',
'South Pacific cyclone season',
'South-West Indian Ocean cyclone season',
'Australian region cyclone season',
// [[Category:2023-24 Southern Hemisphere JTWC season track maps]]
'Southern Hemisphere tropical cyclone season',
// parent categories
'Category:University of Wisconsin CIMSS images',
'Category:NRL images of tropical cyclones',
'JTWC Tropical cyclone warning graphic',
'Category:Central Weather Bureau ROC',
'Category:Japan Meteorological Agency',
'Category:Images from the Japan Meteorological Agency',
'Category:Images from the Philippine Atmospheric, Geophysical and Astronomical Services Administration' ]
//
.run_serial(function(run_next, parent_category_name) {
if (parent_category_name.startsWith('Category:')) {
// 登記。
category_to_parent_hash[parent_category_name] = parent_category_name;
} else {
parent_category_name = get_year_range(parent_category_name) + ' '
+ parent_category_name;
}
// console.log(parent_category_name);
wiki.categorymembers(parent_category_name, function(pages, error) {
pages.forEach(function(page_data) {
if (page_data.ns === CeL.wiki.namespace('Category')) {
category_to_parent_hash[page_data.title]
// register categories
= parent_category_name;
}
});
run_next();
}, {
limit : 'max'
});
}, main_work);
function check_category_exists(category_name) {
if (!(category_name in category_to_parent_hash)) {
CeL.warn('check_category_exists: Category does not exist: '
+ CeL.wiki.title_link_of(category_name));
}
}
function normalize_name(name) {
if (!name)
return name;
return CeL.wiki.upper_case_initial(name.trim().toLowerCase());
}
// auto-search category ends with " name (year)"
function search_category_by_name(TD_name, media_data) {
var date = ' (' + (media_data.date || new Date).getUTCFullYear() + ')';
var footer = ' '
// e.g., " Mun (2019)"
+ normalize_name(TD_name) + date;
// console.log(footer);
if (Object.keys(category_to_parent_hash)
// e.g., [[Category:Tropical Storm Mun (2019)]]
.some(function(category_name) {
if (category_name.endsWith(footer)) {
// media_data.link will be auto-added to media_data.categories
media_data.link = category_name.replace('Category:', '');
return true;
}
})) {
return media_data.link;
}
var link = media_data.name
// relief measures 救濟措施 only for significance hurricane.
// maybe comming here
&& media_data.name.match(/(Hurricane|Typhoon) (\w+)/i);
if (link) {
link = normalize_name(link[1]) + ' ' + normalize_name(link[2]) + date;
// e.g., link === "Hurricane Barbara (2019)"
media_data.link = link;
return link;
}
}
function check_result(operator) {
try {
var result = operator();
if (CeL.is_thenable(result)) {
return result['catch'](function(error) {
console.error(error);
});
}
} catch (e) {
console.error(e);
}
}
function main_work() {
if (CeL.is_debug())
console.log(category_to_parent_hash);
var site_mapping = {
NHC : start_NHC,
JTWC : start_JTWC,
NRL : start_NRL,
CIMSS : start_CIMSS,
// CWB, JMA 在颱風命名後無法取得命名前之編號,因此颱風命名後會採用另一個檔案名稱。
CWB : start_CWB,
// tagged with "All Rights Reserved"...
JMA : start_JMA,
PAGASA : start_PAGASA
};
if (CeL.env.arg_hash) {
var site = CeL.env.arg_hash.site;
if (site) {
site = site_mapping[site.toUpperCase()];
if (site) {
check_result(site);
} else {
CeL.error('Invalid site: ' + site);
}
return;
}
Object.keys(CeL.env.arg_hash).forEach(function(arg_name) {
// console.log(arg_name);
if (CeL.env.arg_hash[arg_name] === true
// e.g., `node 20190629.import_tropical_cyclone_images debug nhc`
&& (arg_name = site_mapping[arg_name.toUpperCase()])) {
check_result(arg_name);
site = true;
}
});
if (site)
return;
}
// for debug:
// return;
for (site in site_mapping) {
Promise.resolve(site_mapping[site]())['catch'](console.error);
}
}
// ------------------------------------------------------------------
function of_wiki_link(media_data) {
var name = media_data.name;
if (media_data.type) {
// normalize type
media_data.type = media_data.type.trim().toLowerCase();
name = media_data.type + ' ' + name;
}
var wiki_link = name ? media_data.link ? CeL.wiki.title_link_of(':en:'
+ media_data.link, name) : name : '';
wiki_link = wiki_link || name ? ' of '
+ (media_data.area ? media_data.area + ' ' : '')
+ (wiki_link || name) : '';
if (media_data.NO >= 1)
wiki_link += ' #' + media_data.NO;
if (!media_data.variable_Map)
media_data.variable_Map = new CeL.wiki.Variable_Map();
media_data.variable_Map.set('wiki_link', wiki_link);
return wiki_link;
}
function fill_type_name(media_data) {
if (media_data.type_name)
return media_data.type_name;
if (media_data.type) {
if (media_data.type.includes('hurricane'))
media_data.type_name = 'hurricane';
else if (media_data.type.includes('typhoon'))
media_data.type_name = 'typhoon';
if (media_data.type_name)
return media_data.type_name;
}
var area = media_data.area.toLowerCase();
// [[Category:2019 Atlantic hurricane season]]
media_data.type_name = area.includes('atlantic')
// 颱風(英語:typhoon)限於赤道以北及國際換日線以西的太平洋及南海水域。於赤道以北及國際換日線以東的太平洋水域產生的風暴則被稱為颶風(英語:hurricane)
|| area.includes('pacific')
// [[Category:2019 Pacific hurricane season]]
&& (area.includes('eastern') || area.includes('central')) ? 'hurricane'
// [[File:2021 CIMSS 02L Two visible infrared satellite loop.gif]]
// 'indian ocean': e.g., @NRL
: area === 'indian' || area === 'indian ocean' ? 'tropical cyclone'
// [[User talk:Kanashimi/Archive 1#Categories of tropical cyclone seasons]]
// [[User talk:Kanashimi/Archive 1#Wrong names of images]]
: area === 'southern hemisphere' ? 'tropical cyclone'
// [[Category:2019 North Indian Ocean cyclone season]]
// But JTWC using "Northwest Pacific/North Indian Ocean*"
// : area.includes('north indian') ? 'cyclone'
// [[Category:2019 Pacific typhoon season]]
: 'typhoon';
return media_data.type_name;
}
// ------------------------------------------------------------------
// General upload function
function upload_media(media_data) {
// area / basins
var area = media_data.area.toLowerCase();
var track_maps_category =
// But JTWC using "Northwest Pacific/North Indian Ocean*"
// TODO: using `.id`. e.g., "WP0719": Northwest Pacific
// area.includes('north indian') ? 'North Indian Ocean' :
area.includes('pacific') ? 'Pacific'
//
: area.includes('atlantic') ? 'Atlantic'
// [[File:2021 CIMSS 02L Two visible infrared satellite loop.gif]]
// TODO: 'South-West Indian Ocean'
// 'indian ocean': e.g., @NRL
: area === 'indian' || area === 'indian ocean'
// ? 'North Indian Ocean'
// [[User talk:Kanashimi/Archive 1#Categories of tropical cyclone seasons]]
? 'Southern Hemisphere'
// [[File:2019 JTWC 03S forecast map.sh0320.gif]]
: area === 'southern hemisphere' ? 'Southern Hemisphere'
// West Australian, Southern Indian Ocean?
// : area === 'austeast' ? 'Indian Ocean'
//
: null;
if (!track_maps_category) {
CeL.error('upload_media: Unknown area: ' + area);
console.log(media_data);
return;
}
track_maps_category = 'Category:'
//
+ get_year_range(track_maps_category, media_data.date) + ' '
//
+ track_maps_category
// Category:2019 Pacific hurricane season track maps
+ ' ' + fill_type_name(media_data) + ' season';
var explicit_track_maps_category = track_maps_category
+ (media_data.filename.includes('satellite') ? ' satellite images'
: ' track maps');
var categories = media_data.categories ? media_data.categories.clone() : [];
categories.push(explicit_track_maps_category in category_to_parent_hash
// track_maps_category 應該都存在。
// 假如不存在 explicit_track_maps_category 的話,就加入 track_maps_category
// 以確保必定有個歸屬。
// NG: 自動創建 explicit_track_maps_category
? explicit_track_maps_category : track_maps_category);
// console.trace([ explicit_track_maps_category, track_maps_category ]);
if (media_data.link)
categories.push('Category:' + media_data.link);
categories.forEach(check_category_exists);
media_data = Object.assign(Object.create(null), media_data, {
categories : categories,
// test_only : true,
show_message : true,
// must be set to reupload
ignorewarnings : 1,
// 標記此編輯為機器人編輯。
bot : 1,
form_data : {
url_post_processor : function(value, XMLHttp, error) {
if (media_directory)
CeL.write_file(media_directory + media_data.filename,
XMLHttp.responseText);
}
},
structured_data : {
// 描繪內容 (P180) [[Commons:Structured data/Modeling/Depiction]]
depicts : 'Q8092'
}
});
// add datetime stamp
var date = media_data.date.format({
format : '%4Y-%2m-%2d %2H:%2M UTC',
zone : 0
});
if (!media_data.comment.includes(date)) {
media_data.comment = media_data.comment.trim() + ' (' + date + ')';
}
// for debug:
if (CeL.is_debug()) {
console.log(media_data);
return;
}
// CeL.set_debug(9);
wiki.upload(media_data/* , after_upload */);
}
// ============================================================================
var NHC_base_URL;
// Visit tropical cyclone index page and get the recent tropical cyclones data.
function start_NHC() {
var NHC_menu_URL = 'https://www.nhc.noaa.gov/cyclones/';
var parsed_NHC_menu_URL = new URL(NHC_menu_URL);
NHC_base_URL = parsed_NHC_menu_URL.origin;
return fetch(NHC_menu_URL).then(function(response) {
// console.log(response);
return response.text();
}).then(function(html) {
CeL.write_file(data_directory
//
+ (new Date).format('NHC ' + cache_filename_label
//
+ ' menu.html'), html);
// console.log(html);
html.each_between(
//
'<th align="left" nowrap><span style="font-size: 18px;">',
//
null, NHC_for_each_area);
});
}
function parse_NHC_time_string(string) {
// CeL.info('parse_NHC_time_string: ' + string);
var date = CeL.DOM.HTML_to_Unicode(string)
//
.match(/^(\d{1,2}):?(\d{2}(?: AM| PM)?) (UTC|EDT|PDT|HST) ([a-zA-Z\d ]+)$/);
if (date) {
if (!/ 20\d{2}$/.test(date[4]))
date[4] += ' ' + (new Date).getUTCFullYear();
date = date[4] + ' ' + date[1] + ':' + date[2] + ' ' + ({
EDT : 'UTC-4',
PDT : 'UTC-7',
HST : 'UTC-10'
}[date[3]] || date[3]);
// CeL.info('parse_NHC_time_string: ' + date);
date = Date.parse(date);
}
return date;
}
function NHC_for_each_area(html) {
// console.log(html);
// Atlantic (- Caribbean Sea - Gulf of Mexico)
// Eastern North Pacific
// Central North Pacific
var area = html.match(/^[^<\-]*/)[0].trim();
var date;
html.each_between('<span class="tiny">', '</span>', function(token) {
// CeL.info('NHC_for_each_area: ' + token);
date = date || parse_NHC_time_string(token);
});
html.each_between('<!--storm serial number:',
// <!--storm serial number: EP02-->
// <!--storm identification: EP022019 Hurricane Barbara-->
// <!--storm identification: EP022019 Post-Tropical Cyclone Barbara-->
'<!-- END graphcrosslink -->', function(token) {
// EP022019: Eastern Pacific 02, 2019
NHC_for_each_cyclones(token, area, date);
});
}
// 有警報才會顯示連結。
// <a href="/refresh/graphics_ep2+shtml/024116.shtml?cone#contents">
//
// <img src="/...png" ... alt="Warnings and 5-Day Cone"><br clear="left">
// Warnings/Cone<br>Static Images</a>
function NHC_for_each_cyclones(token, area, date) {
// console.log([ token, area, date ]);
// return;
var note = token.between('<strong style="font-weight:bold;">', '</td>');
var matched = note.between(null, '</strong>');
if (matched && (matched = parse_NHC_time_string(matched)))
date = matched;
note = note.between('</strong>').replace(/^\s*<br>/g, '')
//
.replace(/<br><br>/g, '<br>').replace(/<br>/g, '. ')
// remove HTML tags
.replace(/<\/?\w[^<>]*>/g, '').trim().replace(/\s{2,}/g, ' ');
var PATTERN_link = /<a href="([^<>"]+)"[^<>]*>([\s\S]+?)<\/a>/g,
// <!--storm identification: EP022019 Hurricane Barbara-->
id = token.between('<!--storm identification:', '-->').trim();
// Get all Tropical Weather Outlook / Hurricane Static Images
while (matched = PATTERN_link.exec(token)) {
if (!matched[2].endsWith('Static Images'))
continue;
// delete matched.input;
// console.log(matched);
var source_url = NHC_base_URL + matched[1];
var media_data = {
name : id,
area : area,
date : date,
source_url : source_url,
note : note
};
get_NHC_Static_Images(media_data);
}
}
// ------------------------------------------------------------------
// Visit all "Warnings/Cone Static Images" pages.
function get_NHC_Static_Images(media_data) {
return fetch(media_data.source_url).then(function(response) {
return response.text();
}).then(function(html) {
CeL.write_file(data_directory
//
+ (new Date).format('NHC ' + cache_filename_label
//
+ ' cyclones.html'), html);
parse_NHC_Static_Images(media_data, html);
});
}
function parse_NHC_Static_Images(media_data, html) {
var link, media_url = html
//
.match(/<img id="coneimage" src *= *"([^<>"]+)"/)[1];
var filename = media_url.match(/\/([^\/]+?)\+png\/[^\/]+?\.png$/)[1]
.replace(/_/g, ' ');
media_data.date = media_data.date ? new Date(media_data.date) : new Date;
var name = media_data.name;
if (name) {
// 5-day intensity track
// e.g., id="EP022019 Hurricane Barbara"
// filename="EP022019 5day cone no line and wind"
var matched = name.match(/^\w*/)[0];
if (true) {
// 檔名不添加氣旋名稱,以統一氣旋存活各時期的檔案名稱。
} else if (matched && matched === filename.match(/^\w*/)[0]) {
// "EP022019" → "EP022019 Hurricane Barbara"
filename = filename.replace(/^\w*/, name);
// e.g., "EP022019 Hurricane Barbara 5day cone no line and wind"
} else {
// relief measures 救濟措施 should not go to here
filename += ' (' + name + ')';
// e.g., "EP022019 5day cone no line and wind (EP022019
// Hurricane Barbara)"
}
link = name.match(/ (\w+)$/i);
if (link) {
// e.g., link[1] === "Barbara"
link = search_category_by_name(link[1], media_data);
}
}
// year is included in filename. e.g., "EP022019"
filename = media_data.date.format(filename_prefix) + 'NHC ' + filename
+ '.png';
media_url = NHC_base_URL + media_url;
// console.log(media_url);
if (false) {
CeL.get_URL_cache(media_url, upload_media, {
directory : base_directory,
filename : filename,
reget : true
});
}
var wiki_link = of_wiki_link(media_data);
// National Hurricane Center
var author = '{{label|Q1329523}}';
Object.assign(media_data, {
media_url : media_url,
filename : filename,
author : author,
// type_name : 'hurricane',
license : '{{PD-USGov-NOAA}}',
description : '{{en|' + author
//
+ "'s 5-day track and intensity forecast cone"
+ media_data.variable_Map.format('wiki_link') + '.}}',
// categories : [ '[[Category:Tropical Depression One-E (2018)]]' ],
comment : 'Import NHC tropical cyclone forecast map' + wiki_link + ' '
+ (media_data.note ? media_data.note + ' ' : '') + media_url,
page_text_updater : media_data.variable_Map
// of the 2019 Pacific hurricane season
});
// Fetch the hurricane forecast map and upload it to commons.
upload_media(media_data);
}
// ============================================================================
function start_JTWC() {
// CeL.set_debug(9);
return fetch('https://www.metoc.navy.mil/jtwc/rss/jtwc.rss?' + Date.now())
//
.then(function(response) {
return response.text();
}).then(function(xml) {
// <H1>403 ERROR</H1>
var error = xml.between('<H1>', '</H1>')
// <div id="header"><h1>Server Error</h1></div>
|| xml.between('<h1>', '</h1>');
if (error) {
throw new Error(error);
}
CeL.write_file(data_directory
//
+ (new Date).format('JTWC ' + cache_filename_label
//
+ '.rss.xml'), xml);
xml.each_between('<item>', '</item>', for_each_JTWC_area);
});
}
// ------------------------------------------------------------------
function for_each_JTWC_area(xml) {
// console.log(xml);
var date = new Date(xml.between('<pubDate>', '</pubDate>'));
var area = xml.between('<title>', '</title>');
// ABPW typhoon: Northwest Pacific/North Indian Ocean*
// CPHC hurricane: Central/Eastern Pacific
// ABIO typhoon: Southern Hemisphere
area = area.between('Current ', ' Tropical Systems').replace(/\*$/, '')
|| area;
var media_data = {
date : date,
area : area,
author : '{{label|Q1142111}}',
license : '{{PD-USGov-Air Force}}\n{{PD-USGov-Navy}}',
source_url : 'https://www.metoc.navy.mil/jtwc/jtwc.html'
};
xml = xml.between('<description>', '</description>');
xml = xml.between('<![CDATA[', ']]>') || xml;
// console.log(xml);
xml.each_between(null, '</ul>', function(html) {
for_each_JTWC_cyclone(html, media_data);
});
}
function for_each_JTWC_cyclone(html, media_data) {
// console.log(html);
var media_url = html
// "TC Warning Graphic", "TCFA Graphic"
// TCFA: Tropical Cyclone Formation Alert 熱帯低気圧形成警報
.match(/<a href='([^<>']+)'[^<>]*>TC[^<>]* Graphic<\/a>/);
if (media_url) {
for_each_JTWC_cyclone_image(html, media_data, media_url);
}
media_url = html
// <li><a href='https://www.metoc.navy.mil/jtwc/products/05B_020000sair.jpg'
// target='newwin'>IR Satellite Imagery</a></li>
.match(/<a href='([^<>']+)'[^<>]*>([^<>]* Imagery)<\/a>/);
if (media_url) {
media_url.image_type = media_url[2].trim();
for_each_JTWC_cyclone_image(html, media_data, media_url);
}
}
function for_each_JTWC_cyclone_image(html, media_data, media_url) {
var image_type = media_url.image_type || 'forecast map';
/**
* <code>
https://www.usno.navy.mil/NOOC/nmfc-ph/RSS/jtwc/pubref/3140.html
USCINCPAC INSTRUCTION 3140.1X
Subj: TROPICAL CYCLONE OPERATIONS MANUAL
MANOP Heading Area Covered
ABPW10 PGTW Western Pacific Significant Tropical Weather Advisory
ABIO10 PGTW Indian Ocean Significant Tropical Weather Advisory
WHPN(xx) PHNC Eastern North Pacific Area
WTPN(xx) PGTW Western North Pacific Area
WTIO(xx) PGTW North Indian Ocean
WHPS(xx) PHNC Eastern South Pacific Area
WTPS(xx) PGTW Western South Pacific Area
WTXS(xx) PGTW South Indian Ocean
</code>
*/
media_url = media_url[1].replace('http://', 'https://');
/**
* <code>
"Tropical Depression 05W (Mun) Warning #02 "
"Hurricane 02E (Barbara) Warning #15 "
"Tropical Storm 02E (Barbara) Warning #25 <font color=red><b>Final Warning</b></font></b><br>"
<br>
<p><b>Tropical Depression 08W (Wipha) Warning #14A CORRECTED <font color=red><b>Corrected</b></font> <font color=red><b>Final Warning</b></font></b><br>
<b>Issued at 03/0300Z<b>
<p><b>Tropical Cyclone Formation Alert WTPN21 </b><br>
<b>Issued at 03/1200Z<b>
</code>
*/
var NO, full_name = (html.between(null, '</b>')
// 可能有 "<b><b>Tropical Cyclone 02S (Belna) Warning #03 <b><br>"
|| html.between(null, '<br>'))
// Warnings.
// Warning #05
.replace(/\s+\#(\d+).*/, function(all, _NO) {
NO = _NO;
return '';
}).replace(/<font .+$/, '')
// remove HTML tags
.replace(/<\/?\w[^<>]*>/g, '').replace(/\s+Warning.*$/, '').trim().replace(
/\s{2,}/g, ' ');
// e.g., 'Final Warning', 'Corrected Final Warning'
var note = html.between('<font color=red><b>', 'Issued at').replace(
/<\/?\w[^<>]*>/g, '').trim().replace(/\s{2,}/g, ' ');
// full_name: e.g., "Tropical Depression 07W (Seven)",
// "Tropical Storm 07W (Seven)", "Tropical Storm 07W (Nari)" → "07W"
// 'Tropical Storm 04W (Choi-wan)' → "04W"
// 'Tropical Cyclone Formation Alert WTPN21' → "WTPN21"
// matched: [ all, id, name ]
var id = full_name.match(/\s+(\w+)(?:\s+\(([\w\-]+)\))$/)
|| full_name.match(/\s+([A-Z]+\d+)$/);
// console.log([ full_name, id ]);
// e.g., 'tropical depression'
var type = full_name.slice(0, id.index).toLowerCase().replace(
'formation alert', '').trim();
var name = id[2] || id[1];
id = id[1];
// using original file name
var filename = media_url.match(
// For "Tropical Cyclone Formation Alert WTPN21",
// different alerts using the same id (WTPN21),
// so we should add more note to distinguish one from the other.
// full_name.includes('Formation Alert') ? /[^\/]+\.\w+$/ : /\.\w+$/
// get full file name now
/[^\/]+$/)[0];
if (image_type && image_type.includes('Satellite Imagery')) {
image_type = image_type.replace('Satellite Imagery',
'Satellite Imagery'.toLowerCase());
// "05B_020600sair.jpg", "05B_021200sair.jpg", "05B_021800sair.jpg",
// "05B_04000sair.jpg"
// 之類,後面的序號似乎會隨時間改變。
var matched = filename.replace(/_/g, ' ').match(
/^(.+?) \d{5,6}sair\.([\w]+)$/);
if (matched) {
filename = id.includes(matched[1]) ? matched[2] : matched[1] + '.'
+ matched[2];
}
}
// e.g., "2019 JTWC 07W forecast map.gif"
filename = media_data.date.format(filename_prefix) + 'JTWC ' + id
// + ' warning map'
+ ' ' + image_type + '.' + filename;
if (!name) {
CeL.error('for_each_JTWC_cyclone: No name got for area '
+ media_data.area + '!');
console.log(html);
return;
}
// e.g., https://commons.wikimedia.org/wiki/File:JTWC_wp0519.gif
media_data = Object.assign({
id : id,
name : name,
NO : NO,
type : type,
full_name : full_name,
filename : filename,
media_url : media_url,
}, media_data);
media_data.source_url += '\n' + media_url;
// <b>Issued at 07/2200Z<b>
// <b>Issued at 06/1600Z<b>
var date = html.match(/Issued at (\d{2})\/(\d{2})(\d{2})Z/);
if (date) {
date = new Date(media_data.date.format('%4Y-%2m-' + date[1] + ' '
+ date[2] + ':' + date[3] + ' UTC'));
media_data.date = date;
}
var link = search_category_by_name(name, media_data);
if (!link && name === id[2]) {
// e.g., "Seven" in "Tropical Storm 07W (Seven)":
// No [[Category:Tropical Storm Seven (2019)]],
// Only [[Category:Tropical Storm 07W (2019)]],
// Now test "07W" in "Tropical Storm 07W (Seven)"
link = search_category_by_name(id, media_data);
// link: e.g., "Tropical Depression 07W (2019)"
}
var wiki_link = of_wiki_link(media_data);
Object.assign(media_data, {
// 預測路徑圖
description : '{{en|' + media_data.author + "'s tropical warning"
+ media_data.variable_Map.format('wiki_link') + '.}}',
comment : 'Import JTWC tropical cyclone ' + image_type + wiki_link
+ '. ' + (note ? note + ' ' : ''),
page_text_updater : media_data.variable_Map
// JTWC using the same media_url for specific tropical
// cyclone
// + media_url
});
upload_media(media_data);
}
// ============================================================================
function start_CIMSS() {
var base_URL = 'https://tropic.ssec.wisc.edu/';
var media_data = {
base_URL : base_URL,
// area : '',
author : '{{label|Q2996587}}',
license : '{{UWiscCIMSS}}',
categories : [ 'Category:University of Wisconsin CIMSS images' ],
source_url : base_URL
};
// http://bagong.CIMSS.dost.gov.ph/tropical-cyclone/severe-weather-bulletin
return fetch(media_data.source_url).then(function(response) {
return response.text();
}).then(function(html) {
CeL.write_file(data_directory
//
+ (new Date).format('CIMSS ' + cache_filename_label
//
+ ' menu.html'), html);
// <div class="col-md-12 article-header" id="swb">
var text = html.between('<MAP NAME="storms">', '</MAP>');
if (!text) {
return;
}
text.each_between('<AREA ', '>', function(token) {
// console.log(token);
var matched = token.match(
/**
* <code>
<MAP NAME="storms">
<!-- STORMS LINKS HERE -->
<AREA SHAPE="RECT" COORDS="238,27,258,47" href="#" onclick="javascript:newStormWindow('atlantic','15L','NO')" onmouseover="doTooltip(event,'Tropical Depression OMAR',1)" onmouseout="hideTip()" alt="" >
<AREA SHAPE="RECT" COORDS="615,50,635,70" href="#" onclick="javascript:newStormWindow('westpac','11W','NO')" onmouseover="doTooltip(event,'Typhoon HAISHEN',1)" onmouseout="hideTip()" alt="" >
<AREA SHAPE="RECT" COORDS="154,70,174,90" href="#" onclick="javascript:newStormWindow('eastpac','90E','YES')" onmouseover="doTooltip(event,'Invest Area 90E<br>20200905 0600Z',1)" onmouseout="hideTip()" alt="" >
<AREA SHAPE="default" alt="default" >
</MAP>
*/
/newStormWindow\('([^']+)','([^']+)','NO'\)/);
if (!matched)
return;
media_data.area = media_data._area = matched[1];
media_data.id = matched[2];
matched = media_data.area.match(/(west|east)pac/);
if (matched) {
media_data.area = normalize_name(matched[1]) + 'ern Pacific';
}
matched = token.match(/doTooltip\(event,'([^']+)',/);
matched = matched[1].trim().replace(/\s{2,}/g, ' ');
matched = matched.match(/^(.+) ([^\s]+)$/);
media_data.type = matched[1];
media_data.name = normalize_name(matched[2]);
var _media_data = Object.clone(media_data);
_media_data.source_url += 'real-time/storm.frame.php?&basin='
//
+ media_data._area + '&sname=' + media_data.id
//
+ '&invest=NO&zoom=4&img=7&vars=111110000000000000000&loop=1';
fetch_CIMSS_typhoon_frame(_media_data);
});
});
}
function fetch_CIMSS_typhoon_frame(media_data) {
// console.trace(media_data);
fetch(media_data.source_url).then(function(response) {
return response.text();
}).then(function(html) {
CeL.write_file(data_directory
//
+ (new Date).format('CIMSS ' + cache_filename_label
//
+ ' menu ' + media_data.id + '.html'), html);
// console.trace(html);
for_each_CIMSS_typhoon(media_data, html);
});
}
function for_each_CIMSS_typhoon(media_data, token) {
var media_url = token.match(/ src="([^"]+\/storm\/movies\/MOV8[^"]+)"/);
media_url = media_data.source_url.replace(/[^\/]+$/, '') + media_url[1];
/**
* <code>
<input type="radio" id="VSW" name="VSW" value=" Visible/Shorwave IR Image
20200905/083019UTC " >VIS/SWIR </label>
</code>
*/
var date = token.between('Visible/Shorwave IR Image', '"');
date = date.match(/(\d{4})(\d{2})(\d{2})\/(\d{2})(\d{2})(\d{2})(UTC)/);
if (!date) {
console.log(token);
console.trace(media_data);
}
date = new Date(date.slice(1, 4).join('-') + ' '
+ date.slice(4, 7).join(':') + ' ' + date[7]);
Object.assign(media_data, {
date : date,
media_url : media_url,
filename : date.format(filename_prefix) + 'CIMSS ' + media_data.id
+ ' ' + media_data.name + ' visible infrared satellite loop'
// .GIF → .gif
+ media_url.match(/\.\w+$/)[0].toLowerCase()
});
media_data.source_url += '\n' + media_url;
search_category_by_name(media_data.name, media_data);
var wiki_link = of_wiki_link(media_data);
var note;
Object.assign(media_data, {
description : '{{en|' + media_data.author
+ "'s visible infrared satellite loop"
+ media_data.variable_Map.format('wiki_link') + '.}}',
comment :
// comment won't accept templates and external links
'Import CIMSS tropical cyclone visible infrared satellite loop'
+ wiki_link + '. ' + (note ? note + ' ' : ''),
page_text_updater : media_data.variable_Map
}, media_data);