mirror of
https://github.com/BigBodyCobain/Shadowbroker.git
synced 2026-04-24 03:26:06 +02:00
Merge pull request #76 from adust09/feat/east-asia-enhancement
feat: East Asia intelligence coverage enhancement
This commit is contained in:
@@ -64,6 +64,36 @@
|
||||
"name": "Stars and Stripes",
|
||||
"url": "https://www.stripes.com/feeds/pacific.rss",
|
||||
"weight": 4
|
||||
},
|
||||
{
|
||||
"name": "Yonhap",
|
||||
"url": "https://en.yna.co.kr/RSS/news.xml",
|
||||
"weight": 4
|
||||
},
|
||||
{
|
||||
"name": "Nikkei Asia",
|
||||
"url": "https://asia.nikkei.com/rss",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"name": "Taipei Times",
|
||||
"url": "https://www.taipeitimes.com/xml/pda.rss",
|
||||
"weight": 4
|
||||
},
|
||||
{
|
||||
"name": "Asia Times",
|
||||
"url": "https://asiatimes.com/feed/",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"name": "Defense News",
|
||||
"url": "https://www.defensenews.com/arc/outboundfeeds/rss/",
|
||||
"weight": 3
|
||||
},
|
||||
{
|
||||
"name": "Japan Times",
|
||||
"url": "https://www.japantimes.co.jp/feed/",
|
||||
"weight": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -142,5 +142,587 @@
|
||||
"branch": "navy",
|
||||
"lat": -7.316,
|
||||
"lng": 72.411
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Fuzhou Changle Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 25.935,
|
||||
"lng": 119.663
|
||||
},
|
||||
{
|
||||
"name": "Longtian Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 25.674,
|
||||
"lng": 119.507
|
||||
},
|
||||
{
|
||||
"name": "Huian Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 25.028,
|
||||
"lng": 118.802
|
||||
},
|
||||
{
|
||||
"name": "Zhangzhou Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 24.575,
|
||||
"lng": 117.588
|
||||
},
|
||||
{
|
||||
"name": "Suixi Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 21.374,
|
||||
"lng": 110.228
|
||||
},
|
||||
{
|
||||
"name": "Nanning Wuxu Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 22.609,
|
||||
"lng": 108.171
|
||||
},
|
||||
{
|
||||
"name": "Jinan Yaoqiang Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 36.857,
|
||||
"lng": 117.216
|
||||
},
|
||||
{
|
||||
"name": "Wuhan Wangjiadun Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 30.500,
|
||||
"lng": 114.211
|
||||
},
|
||||
{
|
||||
"name": "Changsha Datuopu Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 28.068,
|
||||
"lng": 112.866
|
||||
},
|
||||
{
|
||||
"name": "Dingxin Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 40.296,
|
||||
"lng": 99.750
|
||||
},
|
||||
{
|
||||
"name": "Hotan Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF",
|
||||
"branch": "air_force",
|
||||
"lat": 37.039,
|
||||
"lng": 79.866
|
||||
},
|
||||
{
|
||||
"name": "Lingshui Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAAF / PLAN",
|
||||
"branch": "air_force",
|
||||
"lat": 18.507,
|
||||
"lng": 110.034
|
||||
},
|
||||
{
|
||||
"name": "Sanya Phoenix Air Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN Aviation",
|
||||
"branch": "air_force",
|
||||
"lat": 18.303,
|
||||
"lng": 109.412
|
||||
},
|
||||
{
|
||||
"name": "Zhanjiang Naval Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN Southern Theater",
|
||||
"branch": "navy",
|
||||
"lat": 21.190,
|
||||
"lng": 110.405
|
||||
},
|
||||
{
|
||||
"name": "Qingdao / Jiaozhou Naval Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN Northern Theater",
|
||||
"branch": "navy",
|
||||
"lat": 36.100,
|
||||
"lng": 120.272
|
||||
},
|
||||
{
|
||||
"name": "Ningbo / Zhoushan Naval Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN Eastern Theater",
|
||||
"branch": "navy",
|
||||
"lat": 29.949,
|
||||
"lng": 122.094
|
||||
},
|
||||
{
|
||||
"name": "Yulin Naval Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN (SSBN)",
|
||||
"branch": "navy",
|
||||
"lat": 18.226,
|
||||
"lng": 109.557
|
||||
},
|
||||
{
|
||||
"name": "Shanghai Wusong Naval Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN Eastern Theater",
|
||||
"branch": "navy",
|
||||
"lat": 31.398,
|
||||
"lng": 121.503
|
||||
},
|
||||
{
|
||||
"name": "Dalian Naval Shipyard",
|
||||
"country": "China",
|
||||
"operator": "PLAN (carrier construction)",
|
||||
"branch": "navy",
|
||||
"lat": 38.936,
|
||||
"lng": 121.625
|
||||
},
|
||||
{
|
||||
"name": "Jiangnan Shipyard (Changxing Island)",
|
||||
"country": "China",
|
||||
"operator": "PLAN (carrier/destroyer construction)",
|
||||
"branch": "navy",
|
||||
"lat": 31.358,
|
||||
"lng": 121.746
|
||||
},
|
||||
{
|
||||
"name": "Xiangshan Naval Base",
|
||||
"country": "China",
|
||||
"operator": "PLAN Eastern Theater",
|
||||
"branch": "navy",
|
||||
"lat": 29.480,
|
||||
"lng": 121.933
|
||||
},
|
||||
{
|
||||
"name": "Woody Island (Yongxing)",
|
||||
"country": "China",
|
||||
"operator": "PLA Sansha Garrison",
|
||||
"branch": "navy",
|
||||
"lat": 16.833,
|
||||
"lng": 112.333
|
||||
},
|
||||
{
|
||||
"name": "Fiery Cross Reef",
|
||||
"country": "China",
|
||||
"operator": "PLA (SCS outpost)",
|
||||
"branch": "air_force",
|
||||
"lat": 9.550,
|
||||
"lng": 112.892
|
||||
},
|
||||
{
|
||||
"name": "Subi Reef",
|
||||
"country": "China",
|
||||
"operator": "PLA (SCS outpost)",
|
||||
"branch": "air_force",
|
||||
"lat": 10.923,
|
||||
"lng": 114.083
|
||||
},
|
||||
{
|
||||
"name": "Mischief Reef",
|
||||
"country": "China",
|
||||
"operator": "PLA (SCS outpost)",
|
||||
"branch": "navy",
|
||||
"lat": 9.904,
|
||||
"lng": 115.536
|
||||
},
|
||||
{
|
||||
"name": "Dehua PLARF Base (Base 613)",
|
||||
"country": "China",
|
||||
"operator": "PLARF 61 Base",
|
||||
"branch": "missile",
|
||||
"lat": 25.494,
|
||||
"lng": 118.241
|
||||
},
|
||||
{
|
||||
"name": "Huangshan PLARF Base (Base 612)",
|
||||
"country": "China",
|
||||
"operator": "PLARF 61 Base",
|
||||
"branch": "missile",
|
||||
"lat": 29.714,
|
||||
"lng": 118.337
|
||||
},
|
||||
{
|
||||
"name": "Jiande PLARF Base",
|
||||
"country": "China",
|
||||
"operator": "PLARF 61 Base",
|
||||
"branch": "missile",
|
||||
"lat": 29.480,
|
||||
"lng": 119.281
|
||||
},
|
||||
{
|
||||
"name": "Ganzhou PLARF Base",
|
||||
"country": "China",
|
||||
"operator": "PLARF 61 Base",
|
||||
"branch": "missile",
|
||||
"lat": 25.831,
|
||||
"lng": 114.935
|
||||
},
|
||||
{
|
||||
"name": "Meizhou PLARF Base",
|
||||
"country": "China",
|
||||
"operator": "PLARF 61 Base",
|
||||
"branch": "missile",
|
||||
"lat": 24.288,
|
||||
"lng": 116.122
|
||||
},
|
||||
{
|
||||
"name": "Leping PLARF Base",
|
||||
"country": "China",
|
||||
"operator": "PLARF 62 Base",
|
||||
"branch": "missile",
|
||||
"lat": 28.963,
|
||||
"lng": 117.129
|
||||
},
|
||||
{
|
||||
"name": "Nanyang PLARF Base",
|
||||
"country": "China",
|
||||
"operator": "PLARF 63 Base",
|
||||
"branch": "missile",
|
||||
"lat": 33.003,
|
||||
"lng": 112.528
|
||||
},
|
||||
{
|
||||
"name": "Luoyang PLARF Base",
|
||||
"country": "China",
|
||||
"operator": "PLARF 63 Base",
|
||||
"branch": "missile",
|
||||
"lat": 34.620,
|
||||
"lng": 112.454
|
||||
},
|
||||
{
|
||||
"name": "Haiyang PLARF Base (ICBM)",
|
||||
"country": "China",
|
||||
"operator": "PLARF 65 Base",
|
||||
"branch": "missile",
|
||||
"lat": 36.776,
|
||||
"lng": 121.158
|
||||
},
|
||||
{
|
||||
"name": "Sundian PLARF Base (ICBM)",
|
||||
"country": "China",
|
||||
"operator": "PLARF 66 Base",
|
||||
"branch": "missile",
|
||||
"lat": 40.200,
|
||||
"lng": 113.200
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Vladivostok (Pacific Fleet HQ)",
|
||||
"country": "Russia",
|
||||
"operator": "Russian Pacific Fleet",
|
||||
"branch": "navy",
|
||||
"lat": 43.114,
|
||||
"lng": 131.885
|
||||
},
|
||||
{
|
||||
"name": "Vilyuchinsk SSBN Base",
|
||||
"country": "Russia",
|
||||
"operator": "Russian Pacific Fleet (SSBN)",
|
||||
"branch": "navy",
|
||||
"lat": 52.929,
|
||||
"lng": 158.405
|
||||
},
|
||||
{
|
||||
"name": "Yelizovo Air Base (Kamchatka)",
|
||||
"country": "Russia",
|
||||
"operator": "Russian VKS / Pacific Fleet Aviation",
|
||||
"branch": "air_force",
|
||||
"lat": 53.167,
|
||||
"lng": 158.454
|
||||
},
|
||||
{
|
||||
"name": "Khabarovsk-Bolshoy Air Base",
|
||||
"country": "Russia",
|
||||
"operator": "Russian VKS",
|
||||
"branch": "air_force",
|
||||
"lat": 48.528,
|
||||
"lng": 135.188
|
||||
},
|
||||
{
|
||||
"name": "Ukrainka Air Base (Tu-95 bomber)",
|
||||
"country": "Russia",
|
||||
"operator": "Russian VKS Long-Range Aviation",
|
||||
"branch": "air_force",
|
||||
"lat": 51.170,
|
||||
"lng": 128.400
|
||||
},
|
||||
{
|
||||
"name": "Fokino Naval Base",
|
||||
"country": "Russia",
|
||||
"operator": "Russian Pacific Fleet",
|
||||
"branch": "navy",
|
||||
"lat": 42.961,
|
||||
"lng": 132.447
|
||||
},
|
||||
{
|
||||
"name": "Sovgavan Naval Base",
|
||||
"country": "Russia",
|
||||
"operator": "Russian Pacific Fleet",
|
||||
"branch": "navy",
|
||||
"lat": 48.966,
|
||||
"lng": 140.291
|
||||
},
|
||||
{
|
||||
"name": "Vozdvizhenka Air Base",
|
||||
"country": "Russia",
|
||||
"operator": "Russian VKS",
|
||||
"branch": "air_force",
|
||||
"lat": 43.907,
|
||||
"lng": 131.984
|
||||
},
|
||||
{
|
||||
"name": "Etorofu / Iturup (Kuril Islands)",
|
||||
"country": "Russia",
|
||||
"operator": "Russian Army 18th MG Div",
|
||||
"branch": "army",
|
||||
"lat": 44.927,
|
||||
"lng": 147.863
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Yongbyon Nuclear Complex",
|
||||
"country": "North Korea",
|
||||
"operator": "DPRK Nuclear Program",
|
||||
"branch": "nuclear",
|
||||
"lat": 39.796,
|
||||
"lng": 125.754
|
||||
},
|
||||
{
|
||||
"name": "Punggye-ri Nuclear Test Site",
|
||||
"country": "North Korea",
|
||||
"operator": "DPRK Nuclear Program",
|
||||
"branch": "nuclear",
|
||||
"lat": 41.281,
|
||||
"lng": 129.104
|
||||
},
|
||||
{
|
||||
"name": "Sinpo Submarine Base",
|
||||
"country": "North Korea",
|
||||
"operator": "KPN (SLBM)",
|
||||
"branch": "navy",
|
||||
"lat": 40.024,
|
||||
"lng": 128.178
|
||||
},
|
||||
{
|
||||
"name": "Sohae Satellite Launching Station",
|
||||
"country": "North Korea",
|
||||
"operator": "DPRK NADA",
|
||||
"branch": "missile",
|
||||
"lat": 39.660,
|
||||
"lng": 124.705
|
||||
},
|
||||
{
|
||||
"name": "Tonghae Satellite Launching Ground",
|
||||
"country": "North Korea",
|
||||
"operator": "DPRK NADA",
|
||||
"branch": "missile",
|
||||
"lat": 40.856,
|
||||
"lng": 129.665
|
||||
},
|
||||
{
|
||||
"name": "Kaechon Air Base",
|
||||
"country": "North Korea",
|
||||
"operator": "KPAF",
|
||||
"branch": "air_force",
|
||||
"lat": 39.752,
|
||||
"lng": 125.890
|
||||
},
|
||||
{
|
||||
"name": "Wonsan-Kalma Air Base",
|
||||
"country": "North Korea",
|
||||
"operator": "KPAF",
|
||||
"branch": "air_force",
|
||||
"lat": 39.167,
|
||||
"lng": 127.487
|
||||
},
|
||||
{
|
||||
"name": "Nampo Naval Base",
|
||||
"country": "North Korea",
|
||||
"operator": "KPN West Fleet",
|
||||
"branch": "navy",
|
||||
"lat": 38.737,
|
||||
"lng": 125.408
|
||||
},
|
||||
{
|
||||
"name": "Haeju Forward Naval Base",
|
||||
"country": "North Korea",
|
||||
"operator": "KPN",
|
||||
"branch": "navy",
|
||||
"lat": 38.033,
|
||||
"lng": 125.714
|
||||
},
|
||||
{
|
||||
"name": "Koksan Long-Range Artillery Base",
|
||||
"country": "North Korea",
|
||||
"operator": "KPA Artillery Corps",
|
||||
"branch": "army",
|
||||
"lat": 38.330,
|
||||
"lng": 126.586
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Hualien Air Force Base (Jiashan)",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCAF 5th TFW",
|
||||
"branch": "air_force",
|
||||
"lat": 24.024,
|
||||
"lng": 121.617
|
||||
},
|
||||
{
|
||||
"name": "Ching Chuan Kang Air Base",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCAF 3rd TFW",
|
||||
"branch": "air_force",
|
||||
"lat": 24.264,
|
||||
"lng": 120.621
|
||||
},
|
||||
{
|
||||
"name": "Zuoying Naval Base",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCN Fleet Command",
|
||||
"branch": "navy",
|
||||
"lat": 22.702,
|
||||
"lng": 120.268
|
||||
},
|
||||
{
|
||||
"name": "Tainan Air Force Base",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCAF 1st TFW",
|
||||
"branch": "air_force",
|
||||
"lat": 22.951,
|
||||
"lng": 120.206
|
||||
},
|
||||
{
|
||||
"name": "Pingtung Air Base (South)",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCAF 6th Mixed Wing",
|
||||
"branch": "air_force",
|
||||
"lat": 22.673,
|
||||
"lng": 120.462
|
||||
},
|
||||
{
|
||||
"name": "Hsinchu Air Force Base",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCAF 2nd TFW",
|
||||
"branch": "air_force",
|
||||
"lat": 24.818,
|
||||
"lng": 120.939
|
||||
},
|
||||
{
|
||||
"name": "Suao Naval Base",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCN 168th Fleet",
|
||||
"branch": "navy",
|
||||
"lat": 24.594,
|
||||
"lng": 121.862
|
||||
},
|
||||
{
|
||||
"name": "Taitung Zhihang Air Base",
|
||||
"country": "Taiwan",
|
||||
"operator": "ROCAF 7th FTG",
|
||||
"branch": "air_force",
|
||||
"lat": 22.793,
|
||||
"lng": 121.182
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Subic Bay (EDCA site)",
|
||||
"country": "Philippines",
|
||||
"operator": "Philippine Navy / US EDCA",
|
||||
"branch": "navy",
|
||||
"lat": 14.794,
|
||||
"lng": 120.281
|
||||
},
|
||||
{
|
||||
"name": "Clark Air Base (EDCA site)",
|
||||
"country": "Philippines",
|
||||
"operator": "Philippine Air Force / US EDCA",
|
||||
"branch": "air_force",
|
||||
"lat": 15.186,
|
||||
"lng": 120.560
|
||||
},
|
||||
{
|
||||
"name": "Basa Air Base",
|
||||
"country": "Philippines",
|
||||
"operator": "Philippine Air Force",
|
||||
"branch": "air_force",
|
||||
"lat": 14.988,
|
||||
"lng": 120.493
|
||||
},
|
||||
{
|
||||
"name": "Antonio Bautista Air Base (Palawan)",
|
||||
"country": "Philippines",
|
||||
"operator": "Philippine Air Force",
|
||||
"branch": "air_force",
|
||||
"lat": 9.742,
|
||||
"lng": 118.759
|
||||
},
|
||||
{
|
||||
"name": "Lal-lo (EDCA site, Cagayan)",
|
||||
"country": "Philippines",
|
||||
"operator": "Philippine Army / US EDCA",
|
||||
"branch": "army",
|
||||
"lat": 18.200,
|
||||
"lng": 121.659
|
||||
},
|
||||
{
|
||||
"name": "Balabac Naval Station",
|
||||
"country": "Philippines",
|
||||
"operator": "Philippine Navy",
|
||||
"branch": "navy",
|
||||
"lat": 7.986,
|
||||
"lng": 117.062
|
||||
},
|
||||
|
||||
{
|
||||
"name": "RAAF Base Darwin",
|
||||
"country": "Australia",
|
||||
"operator": "RAAF / USMC Rotational",
|
||||
"branch": "air_force",
|
||||
"lat": -12.415,
|
||||
"lng": 130.876
|
||||
},
|
||||
{
|
||||
"name": "RAAF Base Tindal",
|
||||
"country": "Australia",
|
||||
"operator": "RAAF / USAF Rotational",
|
||||
"branch": "air_force",
|
||||
"lat": -14.521,
|
||||
"lng": 132.378
|
||||
},
|
||||
{
|
||||
"name": "HMAS Stirling (Garden Island)",
|
||||
"country": "Australia",
|
||||
"operator": "RAN / AUKUS submarine base",
|
||||
"branch": "navy",
|
||||
"lat": -32.235,
|
||||
"lng": 115.691
|
||||
},
|
||||
{
|
||||
"name": "Pine Gap (Joint Intelligence)",
|
||||
"country": "Australia",
|
||||
"operator": "ASD / CIA Joint Facility",
|
||||
"branch": "army",
|
||||
"lat": -23.799,
|
||||
"lng": 133.737
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,646 @@
|
||||
{
|
||||
"412000001": {
|
||||
"hull_number": "101",
|
||||
"name": "Nanchang",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000002": {
|
||||
"hull_number": "102",
|
||||
"name": "Lhasa",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000003": {
|
||||
"hull_number": "103",
|
||||
"name": "Anshan",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000004": {
|
||||
"hull_number": "104",
|
||||
"name": "Wuxi",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000005": {
|
||||
"hull_number": "105",
|
||||
"name": "Dalian",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000006": {
|
||||
"hull_number": "106",
|
||||
"name": "Yan'an",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000007": {
|
||||
"hull_number": "107",
|
||||
"name": "Zunyi",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000008": {
|
||||
"hull_number": "108",
|
||||
"name": "Xianyang",
|
||||
"class": "Type 055",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_055_destroyer"
|
||||
},
|
||||
"412000101": {
|
||||
"hull_number": "117",
|
||||
"name": "Xining",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000102": {
|
||||
"hull_number": "118",
|
||||
"name": "Urumqi",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000103": {
|
||||
"hull_number": "119",
|
||||
"name": "Guiyang",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000104": {
|
||||
"hull_number": "120",
|
||||
"name": "Chengdu",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000105": {
|
||||
"hull_number": "131",
|
||||
"name": "Taiyuan",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000106": {
|
||||
"hull_number": "132",
|
||||
"name": "Suzhou",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000107": {
|
||||
"hull_number": "133",
|
||||
"name": "Nantong",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000108": {
|
||||
"hull_number": "134",
|
||||
"name": "Suqian",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000109": {
|
||||
"hull_number": "135",
|
||||
"name": "Lianyungang",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000110": {
|
||||
"hull_number": "136",
|
||||
"name": "Xuchang",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000111": {
|
||||
"hull_number": "155",
|
||||
"name": "Nanjing",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000112": {
|
||||
"hull_number": "156",
|
||||
"name": "Zibo",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000113": {
|
||||
"hull_number": "157",
|
||||
"name": "Lishui",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000114": {
|
||||
"hull_number": "161",
|
||||
"name": "Hohhot",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000115": {
|
||||
"hull_number": "162",
|
||||
"name": "Yancheng",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000116": {
|
||||
"hull_number": "163",
|
||||
"name": "Kaifeng",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000117": {
|
||||
"hull_number": "164",
|
||||
"name": "Taizhou",
|
||||
"class": "Type 052D",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412000201": {
|
||||
"hull_number": "538",
|
||||
"name": "Yantai",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000202": {
|
||||
"hull_number": "539",
|
||||
"name": "Wuhu",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000203": {
|
||||
"hull_number": "540",
|
||||
"name": "Huainan",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000204": {
|
||||
"hull_number": "541",
|
||||
"name": "Huaihua",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000205": {
|
||||
"hull_number": "542",
|
||||
"name": "Zaozhuang",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000206": {
|
||||
"hull_number": "529",
|
||||
"name": "Zhoushan",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000207": {
|
||||
"hull_number": "530",
|
||||
"name": "Xuzhou",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000208": {
|
||||
"hull_number": "531",
|
||||
"name": "Xiangtan",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000209": {
|
||||
"hull_number": "532",
|
||||
"name": "Jingzhou",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000210": {
|
||||
"hull_number": "536",
|
||||
"name": "Xuchang",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000211": {
|
||||
"hull_number": "546",
|
||||
"name": "Yancheng",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000212": {
|
||||
"hull_number": "547",
|
||||
"name": "Linyi",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000213": {
|
||||
"hull_number": "548",
|
||||
"name": "Yiyang",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000214": {
|
||||
"hull_number": "549",
|
||||
"name": "Changzhou",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000215": {
|
||||
"hull_number": "550",
|
||||
"name": "Weifang",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000301": {
|
||||
"hull_number": "31",
|
||||
"name": "Hainan",
|
||||
"class": "Type 075",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_075_landing_helicopter_dock"
|
||||
},
|
||||
"412000302": {
|
||||
"hull_number": "32",
|
||||
"name": "Guangxi",
|
||||
"class": "Type 075",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_075_landing_helicopter_dock"
|
||||
},
|
||||
"412000303": {
|
||||
"hull_number": "33",
|
||||
"name": "Anhui",
|
||||
"class": "Type 075",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_075_landing_helicopter_dock"
|
||||
},
|
||||
"412000401": {
|
||||
"hull_number": "16",
|
||||
"name": "Liaoning",
|
||||
"class": "Type 001",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Chinese_aircraft_carrier_Liaoning"
|
||||
},
|
||||
"412000402": {
|
||||
"hull_number": "17",
|
||||
"name": "Shandong",
|
||||
"class": "Type 002",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Chinese_aircraft_carrier_Shandong"
|
||||
},
|
||||
"412000403": {
|
||||
"hull_number": "18",
|
||||
"name": "Fujian",
|
||||
"class": "Type 003",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Chinese_aircraft_carrier_Fujian"
|
||||
},
|
||||
"412000501": {
|
||||
"hull_number": "980",
|
||||
"name": "Hulunhu",
|
||||
"class": "Type 901",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_901_replenishment_ship"
|
||||
},
|
||||
"412000502": {
|
||||
"hull_number": "981",
|
||||
"name": "Chaganhu",
|
||||
"class": "Type 901",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_901_replenishment_ship"
|
||||
},
|
||||
"412000601": {
|
||||
"hull_number": "998",
|
||||
"name": "Kunlun Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000602": {
|
||||
"hull_number": "999",
|
||||
"name": "Jinggang Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000603": {
|
||||
"hull_number": "989",
|
||||
"name": "Changbai Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000604": {
|
||||
"hull_number": "988",
|
||||
"name": "Yimeng Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000605": {
|
||||
"hull_number": "987",
|
||||
"name": "Wuzhi Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000606": {
|
||||
"hull_number": "986",
|
||||
"name": "Longhu Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000607": {
|
||||
"hull_number": "985",
|
||||
"name": "Dabie Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000608": {
|
||||
"hull_number": "984",
|
||||
"name": "Wuyi Shan",
|
||||
"class": "Type 071",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_071_amphibious_transport_dock"
|
||||
},
|
||||
"412000701": {
|
||||
"hull_number": "815A-1",
|
||||
"name": "Dongdiao",
|
||||
"class": "Type 815A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_815_electronic_reconnaissance_ship"
|
||||
},
|
||||
"412000702": {
|
||||
"hull_number": "815A-2",
|
||||
"name": "Haiwangxing",
|
||||
"class": "Type 815A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_815_electronic_reconnaissance_ship"
|
||||
},
|
||||
"412000703": {
|
||||
"hull_number": "815A-3",
|
||||
"name": "Tianwangxing",
|
||||
"class": "Type 815A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_815_electronic_reconnaissance_ship"
|
||||
},
|
||||
"412009001": {
|
||||
"hull_number": "2901",
|
||||
"name": "CCG 2901",
|
||||
"class": "12000-ton Cutter",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009002": {
|
||||
"hull_number": "3901",
|
||||
"name": "CCG 3901",
|
||||
"class": "12000-ton Cutter",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009003": {
|
||||
"hull_number": "1305",
|
||||
"name": "CCG 1305",
|
||||
"class": "Type 818",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009004": {
|
||||
"hull_number": "1306",
|
||||
"name": "CCG 1306",
|
||||
"class": "Type 818",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009005": {
|
||||
"hull_number": "2502",
|
||||
"name": "CCG 2502",
|
||||
"class": "5000-ton Cutter",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009006": {
|
||||
"hull_number": "2302",
|
||||
"name": "CCG 2302",
|
||||
"class": "3000-ton Cutter",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009007": {
|
||||
"hull_number": "2303",
|
||||
"name": "CCG 2303",
|
||||
"class": "3000-ton Cutter",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009008": {
|
||||
"hull_number": "1103",
|
||||
"name": "CCG 1103",
|
||||
"class": "Type 718B",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009009": {
|
||||
"hull_number": "1105",
|
||||
"name": "CCG 1105",
|
||||
"class": "Type 718B",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412009010": {
|
||||
"hull_number": "1302",
|
||||
"name": "CCG 1302",
|
||||
"class": "Type 818",
|
||||
"force": "CCG",
|
||||
"wiki": "https://en.wikipedia.org/wiki/China_Coast_Guard"
|
||||
},
|
||||
"412000801": {
|
||||
"hull_number": "171",
|
||||
"name": "Haikou",
|
||||
"class": "Type 052C",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052C_destroyer"
|
||||
},
|
||||
"412000802": {
|
||||
"hull_number": "170",
|
||||
"name": "Lanzhou",
|
||||
"class": "Type 052C",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052C_destroyer"
|
||||
},
|
||||
"412000803": {
|
||||
"hull_number": "150",
|
||||
"name": "Changchun",
|
||||
"class": "Type 052C",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052C_destroyer"
|
||||
},
|
||||
"412000804": {
|
||||
"hull_number": "151",
|
||||
"name": "Zhengzhou",
|
||||
"class": "Type 052C",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052C_destroyer"
|
||||
},
|
||||
"412000805": {
|
||||
"hull_number": "152",
|
||||
"name": "Jinan",
|
||||
"class": "Type 052C",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052C_destroyer"
|
||||
},
|
||||
"412000806": {
|
||||
"hull_number": "153",
|
||||
"name": "Xi'an",
|
||||
"class": "Type 052C",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052C_destroyer"
|
||||
},
|
||||
"412000901": {
|
||||
"hull_number": "572",
|
||||
"name": "Hengshui",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000902": {
|
||||
"hull_number": "573",
|
||||
"name": "Liuzhou",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000903": {
|
||||
"hull_number": "574",
|
||||
"name": "Sanya",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000904": {
|
||||
"hull_number": "575",
|
||||
"name": "Yueyang",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000905": {
|
||||
"hull_number": "576",
|
||||
"name": "Daqing",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412000906": {
|
||||
"hull_number": "577",
|
||||
"name": "Huanggang",
|
||||
"class": "Type 054A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_054A_frigate"
|
||||
},
|
||||
"412001001": {
|
||||
"hull_number": "500",
|
||||
"name": "Xianfeng",
|
||||
"class": "Type 056A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_056_corvette"
|
||||
},
|
||||
"412001002": {
|
||||
"hull_number": "501",
|
||||
"name": "Xinyang",
|
||||
"class": "Type 056A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_056_corvette"
|
||||
},
|
||||
"412001003": {
|
||||
"hull_number": "502",
|
||||
"name": "Huangshi",
|
||||
"class": "Type 056",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_056_corvette"
|
||||
},
|
||||
"412001004": {
|
||||
"hull_number": "509",
|
||||
"name": "Huaian",
|
||||
"class": "Type 056A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_056_corvette"
|
||||
},
|
||||
"412001005": {
|
||||
"hull_number": "510",
|
||||
"name": "Ningde",
|
||||
"class": "Type 056A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_056_corvette"
|
||||
},
|
||||
"412001101": {
|
||||
"hull_number": "795",
|
||||
"name": "Nanchong",
|
||||
"class": "Type 039A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_039A_submarine"
|
||||
},
|
||||
"412001201": {
|
||||
"hull_number": "892",
|
||||
"name": "Hualuoshan",
|
||||
"class": "Type 903A",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_903_replenishment_ship"
|
||||
},
|
||||
"412001202": {
|
||||
"hull_number": "889",
|
||||
"name": "Taihu",
|
||||
"class": "Type 903",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_903_replenishment_ship"
|
||||
},
|
||||
"412001301": {
|
||||
"hull_number": "636",
|
||||
"name": "Nanning",
|
||||
"class": "Type 052DL",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412001302": {
|
||||
"hull_number": "165",
|
||||
"name": "Zhanjiang",
|
||||
"class": "Type 052DL",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
},
|
||||
"412001303": {
|
||||
"hull_number": "166",
|
||||
"name": "Huainan",
|
||||
"class": "Type 052DL",
|
||||
"force": "PLAN",
|
||||
"wiki": "https://en.wikipedia.org/wiki/Type_052D_destroyer"
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,11 @@ def fetch_ships():
|
||||
for ship in ships:
|
||||
enrich_with_yacht_alert(ship)
|
||||
|
||||
# Enrich ships with PLAN/CCG vessel data
|
||||
from services.fetchers.plan_vessel_alert import enrich_with_plan_vessel
|
||||
for ship in ships:
|
||||
enrich_with_plan_vessel(ship)
|
||||
|
||||
logger.info(f"Ships: {len(carriers)} carriers + {len(ais_vessels)} AIS vessels")
|
||||
with _data_lock:
|
||||
latest_data['ships'] = ships
|
||||
|
||||
@@ -38,6 +38,11 @@ _ICAO_COUNTRY_RANGES = [
|
||||
(0x840000, 0x87FFFF, "Japan", "JSDF"),
|
||||
(0x700000, 0x71FFFF, "South Korea", "ROK"),
|
||||
(0xE80000, 0xE80FFF, "Taiwan", "ROC"),
|
||||
(0x150000, 0x157FFF, "Russia", "VKS"),
|
||||
(0x7C0000, 0x7FFFFF, "Australia", "RAAF"),
|
||||
(0x758000, 0x75FFFF, "Philippines", "PAF"),
|
||||
(0x768000, 0x76FFFF, "Singapore", "RSAF"),
|
||||
(0x720000, 0x727FFF, "North Korea", "KPAF"),
|
||||
]
|
||||
|
||||
|
||||
@@ -66,18 +71,24 @@ def _classify_military_type(raw_model: str) -> str:
|
||||
if any(k in model for k in [
|
||||
"F16", "F35", "F22", "F15", "F18", "T38", "T6", "A10",
|
||||
"J10", "J11", "J15", "J16", "J20", "JF17",
|
||||
"SU27", "SU30", "SU35",
|
||||
"SU27", "SU30", "SU35", "SU57", "MIG29", "MIG31",
|
||||
"F15J", "F2", "IDF", "FA50", "KF21",
|
||||
]):
|
||||
return "fighter"
|
||||
if any(k in model for k in [
|
||||
"TU95", "TU160", "TU22",
|
||||
]):
|
||||
return "bomber"
|
||||
if any(k in model for k in [
|
||||
"C17", "C5", "C130", "C30", "A400", "V22",
|
||||
"Y20", "Y9", "Y8", "C2",
|
||||
"IL76", "AN124", "AN12",
|
||||
]):
|
||||
return "cargo"
|
||||
if any(k in model for k in [
|
||||
"P8", "E3", "E8", "U2",
|
||||
"KJ500", "KJ200", "GX11", "P1", "E767", "E2K", "E2C",
|
||||
"A50", "TU214R", "IL20",
|
||||
]):
|
||||
return "recon"
|
||||
return "default"
|
||||
|
||||
@@ -111,6 +111,22 @@ _KEYWORD_COORDS = {
|
||||
"singapore": (1.352, 103.819),
|
||||
"bangkok": (13.756, 100.501),
|
||||
"jakarta": (-6.208, 106.845),
|
||||
# East Asia — islands, straits, and disputed areas
|
||||
"pratas": (20.71, 116.72),
|
||||
"dongsha": (20.71, 116.72),
|
||||
"kinmen": (24.45, 118.38),
|
||||
"matsu": (26.16, 119.94),
|
||||
"scarborough": (15.14, 117.77),
|
||||
"paracel": (16.50, 112.00),
|
||||
"spratly": (10.00, 114.00),
|
||||
"miyako strait": (24.78, 125.30),
|
||||
"bashi channel": (21.00, 121.50),
|
||||
"luzon strait": (20.50, 121.50),
|
||||
" dmz ": (38.00, 127.00),
|
||||
"yalu": (40.00, 124.40),
|
||||
"yongbyon": (39.80, 125.76),
|
||||
"wonsan": (39.18, 127.48),
|
||||
"busan": (35.18, 129.07),
|
||||
}
|
||||
|
||||
# Immutable after module load — sort by descending keyword length so
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""PLAN/CCG Vessel Alert DB — load and enrich AIS vessels with Chinese navy/coast guard metadata."""
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("services.data_fetcher")
|
||||
|
||||
_PLAN_CCG_DB: dict = {}
|
||||
|
||||
|
||||
def _load_plan_ccg_db():
|
||||
"""Load plan_ccg_vessels.json into memory at import time."""
|
||||
global _PLAN_CCG_DB
|
||||
json_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
||||
"data", "plan_ccg_vessels.json"
|
||||
)
|
||||
if not os.path.exists(json_path):
|
||||
logger.warning(f"PLAN/CCG vessel DB not found at {json_path}")
|
||||
return
|
||||
try:
|
||||
with open(json_path, "r", encoding="utf-8") as fh:
|
||||
_PLAN_CCG_DB.update(json.load(fh))
|
||||
logger.info(f"PLAN/CCG vessel DB loaded: {len(_PLAN_CCG_DB)} vessels")
|
||||
except (IOError, OSError, json.JSONDecodeError, ValueError, KeyError) as e:
|
||||
logger.error(f"Failed to load PLAN/CCG vessel DB: {e}")
|
||||
|
||||
|
||||
_load_plan_ccg_db()
|
||||
|
||||
|
||||
def enrich_with_plan_vessel(ship: dict) -> dict:
|
||||
"""If ship's MMSI is in the PLAN/CCG DB, attach enrichment metadata."""
|
||||
mmsi = str(ship.get("mmsi", "")).strip()
|
||||
if mmsi and mmsi in _PLAN_CCG_DB:
|
||||
info = _PLAN_CCG_DB[mmsi]
|
||||
ship["plan_name"] = info.get("name", "")
|
||||
ship["plan_class"] = info.get("class", "")
|
||||
ship["plan_force"] = info.get("force", "")
|
||||
ship["plan_hull"] = info.get("hull_number", "")
|
||||
ship["plan_wiki"] = info.get("wiki", "")
|
||||
return ship
|
||||
@@ -9,7 +9,7 @@ from pathlib import Path
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_PATH = Path(__file__).parent.parent / "config" / "news_feeds.json"
|
||||
MAX_FEEDS = 20
|
||||
MAX_FEEDS = 25
|
||||
|
||||
DEFAULT_FEEDS = [
|
||||
{"name": "NPR", "url": "https://feeds.npr.org/1004/rss.xml", "weight": 4},
|
||||
@@ -25,6 +25,12 @@ DEFAULT_FEEDS = [
|
||||
{"name": "SCMP", "url": "https://www.scmp.com/rss/91/feed", "weight": 4},
|
||||
{"name": "The Diplomat", "url": "https://thediplomat.com/feed/", "weight": 4},
|
||||
{"name": "Stars and Stripes", "url": "https://www.stripes.com/feeds/pacific.rss", "weight": 4},
|
||||
{"name": "Yonhap", "url": "https://en.yna.co.kr/RSS/news.xml", "weight": 4},
|
||||
{"name": "Nikkei Asia", "url": "https://asia.nikkei.com/rss", "weight": 3},
|
||||
{"name": "Taipei Times", "url": "https://www.taipeitimes.com/xml/pda.rss", "weight": 4},
|
||||
{"name": "Asia Times", "url": "https://asiatimes.com/feed/", "weight": 3},
|
||||
{"name": "Defense News", "url": "https://www.defensenews.com/arc/outboundfeeds/rss/", "weight": 3},
|
||||
{"name": "Japan Times", "url": "https://www.japantimes.co.jp/feed/", "weight": 3},
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,24 @@ class TestEnrichCountry:
|
||||
def test_invalid_hex_with_empty(self):
|
||||
assert _enrich_country("ZZZZ", "") == ("Military Asset", "")
|
||||
|
||||
def test_russia_range(self):
|
||||
assert _enrich_country("150000", "Unknown") == ("Russia", "VKS")
|
||||
|
||||
def test_russia_range_end(self):
|
||||
assert _enrich_country("157FFF", "Unknown") == ("Russia", "VKS")
|
||||
|
||||
def test_australia_range(self):
|
||||
assert _enrich_country("7C0000", "Unknown") == ("Australia", "RAAF")
|
||||
|
||||
def test_philippines_range(self):
|
||||
assert _enrich_country("758000", "Unknown") == ("Philippines", "PAF")
|
||||
|
||||
def test_singapore_range(self):
|
||||
assert _enrich_country("768000", "Unknown") == ("Singapore", "RSAF")
|
||||
|
||||
def test_north_korea_range(self):
|
||||
assert _enrich_country("720000", "Unknown") == ("North Korea", "KPAF")
|
||||
|
||||
|
||||
class TestClassifyMilitaryType:
|
||||
@pytest.mark.parametrize("model,expected", [
|
||||
@@ -52,6 +70,22 @@ class TestClassifyMilitaryType:
|
||||
("H60", "heli"),
|
||||
("K35", "tanker"),
|
||||
("Boeing 737", "default"),
|
||||
# Russian aircraft
|
||||
("SU-27", "fighter"),
|
||||
("SU-30", "fighter"),
|
||||
("SU-35", "fighter"),
|
||||
("SU-57", "fighter"),
|
||||
("MiG-29", "fighter"),
|
||||
("MiG-31", "fighter"),
|
||||
("Tu-95", "bomber"),
|
||||
("Tu-160", "bomber"),
|
||||
("Tu-22", "bomber"),
|
||||
("IL-76", "cargo"),
|
||||
("AN-124", "cargo"),
|
||||
("AN-12", "cargo"),
|
||||
("A-50", "recon"),
|
||||
("Tu-214R", "recon"),
|
||||
("IL-20", "recon"),
|
||||
])
|
||||
def test_classification(self, model: str, expected: str):
|
||||
assert _classify_military_type(model) == expected
|
||||
|
||||
@@ -35,11 +35,22 @@ class TestMilitaryBasesData:
|
||||
assert -180 <= entry["lng"] <= 180, f"{entry['name']} has invalid lng"
|
||||
|
||||
def test_branch_values_are_known(self):
|
||||
known_branches = {"air_force", "navy", "marines", "army"}
|
||||
known_branches = {"air_force", "navy", "marines", "army", "missile", "nuclear"}
|
||||
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
|
||||
for entry in raw:
|
||||
assert entry["branch"] in known_branches, f"{entry['name']} has unknown branch: {entry['branch']}"
|
||||
|
||||
def test_adversary_bases_present(self):
|
||||
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
|
||||
countries = {entry["country"] for entry in raw}
|
||||
for expected in ("China", "Russia", "North Korea", "Taiwan"):
|
||||
assert expected in countries, f"Missing bases for {expected}"
|
||||
|
||||
def test_no_duplicate_names(self):
|
||||
raw = json.loads(BASES_PATH.read_text(encoding="utf-8"))
|
||||
names = [entry["name"] for entry in raw]
|
||||
assert len(names) == len(set(names)), "Duplicate base names found"
|
||||
|
||||
|
||||
class TestFetchMilitaryBases:
|
||||
"""Test the fetcher populates latest_data correctly."""
|
||||
|
||||
@@ -72,6 +72,53 @@ class TestResolveCoords:
|
||||
result = _resolve_coords("visit the uk soon")
|
||||
assert result == (55.378, -3.435)
|
||||
|
||||
# -- New East Asia island/strait keywords ------------------------------------
|
||||
|
||||
def test_pratas(self):
|
||||
assert _resolve_coords("china patrols near pratas islands") == (20.71, 116.72)
|
||||
|
||||
def test_dongsha(self):
|
||||
assert _resolve_coords("dongsha atoll tensions") == (20.71, 116.72)
|
||||
|
||||
def test_kinmen(self):
|
||||
assert _resolve_coords("artillery drill near kinmen") == (24.45, 118.38)
|
||||
|
||||
def test_matsu(self):
|
||||
assert _resolve_coords("matsu island cable cut") == (26.16, 119.94)
|
||||
|
||||
def test_scarborough(self):
|
||||
assert _resolve_coords("scarborough shoal standoff") == (15.14, 117.77)
|
||||
|
||||
def test_paracel(self):
|
||||
assert _resolve_coords("paracel islands dispute") == (16.50, 112.00)
|
||||
|
||||
def test_spratly(self):
|
||||
assert _resolve_coords("spratly island reclamation") == (10.00, 114.00)
|
||||
|
||||
def test_miyako_strait(self):
|
||||
assert _resolve_coords("PLAN warships transit miyako strait") == (24.78, 125.30)
|
||||
|
||||
def test_bashi_channel(self):
|
||||
assert _resolve_coords("submarine detected in bashi channel") == (21.00, 121.50)
|
||||
|
||||
def test_luzon_strait(self):
|
||||
assert _resolve_coords("luzon strait patrol") == (20.50, 121.50)
|
||||
|
||||
def test_dmz(self):
|
||||
assert _resolve_coords("tension at the dmz border") == (38.00, 127.00)
|
||||
|
||||
def test_yalu(self):
|
||||
assert _resolve_coords("troops near yalu river") == (40.00, 124.40)
|
||||
|
||||
def test_yongbyon(self):
|
||||
assert _resolve_coords("activity at yongbyon reactor") == (39.80, 125.76)
|
||||
|
||||
def test_wonsan(self):
|
||||
assert _resolve_coords("missile launch from wonsan") == (39.18, 127.48)
|
||||
|
||||
def test_busan(self):
|
||||
assert _resolve_coords("naval exercise near busan port") == (35.18, 129.07)
|
||||
|
||||
# -- No match --------------------------------------------------------------
|
||||
|
||||
def test_no_match_returns_none(self):
|
||||
@@ -98,5 +145,6 @@ class TestFeedConfig:
|
||||
|
||||
def test_new_east_asia_feeds_present(self):
|
||||
names = {f["name"] for f in DEFAULT_FEEDS}
|
||||
expected = {"FocusTaiwan", "Kyodo", "SCMP", "The Diplomat", "Stars and Stripes"}
|
||||
expected = {"FocusTaiwan", "Kyodo", "SCMP", "The Diplomat", "Stars and Stripes",
|
||||
"Yonhap", "Nikkei Asia", "Taipei Times", "Asia Times", "Defense News", "Japan Times"}
|
||||
assert expected.issubset(names)
|
||||
|
||||
@@ -1475,11 +1475,11 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
id="military-bases-layer"
|
||||
type="circle"
|
||||
paint={{
|
||||
'circle-color': '#ef4444',
|
||||
'circle-color': ['match', ['get', 'side'], 'red', '#ef4444', 'green', '#22c55e', '#3b82f6'],
|
||||
'circle-radius': ['interpolate', ['linear'], ['zoom'], 2, 4, 6, 7, 10, 10],
|
||||
'circle-opacity': 0.8,
|
||||
'circle-stroke-width': 2,
|
||||
'circle-stroke-color': '#fca5a5',
|
||||
'circle-stroke-color': ['match', ['get', 'side'], 'red', '#fca5a5', 'green', '#86efac', '#93c5fd'],
|
||||
}}
|
||||
/>
|
||||
<Layer
|
||||
@@ -1494,7 +1494,7 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
'text-allow-overlap': false,
|
||||
}}
|
||||
paint={{
|
||||
'text-color': '#fca5a5',
|
||||
'text-color': ['match', ['get', 'side'], 'red', '#fca5a5', 'green', '#86efac', '#93c5fd'],
|
||||
'text-halo-color': 'rgba(0,0,0,0.9)',
|
||||
'text-halo-width': 1,
|
||||
}}
|
||||
@@ -1890,7 +1890,15 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
if (!base) return null;
|
||||
const branchLabel: Record<string, string> = {
|
||||
air_force: 'AIR FORCE', navy: 'NAVY', marines: 'MARINES', army: 'ARMY',
|
||||
missile: 'MISSILE FORCES', nuclear: 'NUCLEAR FACILITY',
|
||||
};
|
||||
const isAdversary = ['China', 'Russia', 'North Korea'].includes(base.country);
|
||||
const isROC = base.country === 'Taiwan';
|
||||
const accentColor = isAdversary ? 'red' : isROC ? 'green' : 'blue';
|
||||
const borderCls = isAdversary ? 'border-red-400/40' : isROC ? 'border-green-400/40' : 'border-blue-400/40';
|
||||
const textCls = isAdversary ? 'text-[#fca5a5]' : isROC ? 'text-[#86efac]' : 'text-[#93c5fd]';
|
||||
const titleCls = isAdversary ? 'text-red-400 border-b border-red-400/20' : isROC ? 'text-green-400 border-b border-green-400/20' : 'text-blue-400 border-b border-blue-400/20';
|
||||
const footerCls = isAdversary ? 'text-red-600' : isROC ? 'text-green-600' : 'text-blue-600';
|
||||
return (
|
||||
<Popup
|
||||
longitude={base.lng}
|
||||
@@ -1901,8 +1909,8 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
className="threat-popup"
|
||||
maxWidth="280px"
|
||||
>
|
||||
<div className="map-popup bg-[#1a1035] border border-red-400/40 text-[#fca5a5] min-w-[200px]">
|
||||
<div className="map-popup-title text-red-400 border-b border-red-400/20 pb-1">
|
||||
<div className={`map-popup bg-[#1a1035] border ${borderCls} ${textCls} min-w-[200px]`}>
|
||||
<div className={`map-popup-title ${titleCls} pb-1`}>
|
||||
{base.name}
|
||||
</div>
|
||||
<div className="map-popup-row">
|
||||
@@ -1911,7 +1919,7 @@ const MaplibreViewer = ({ data, activeLayers, onEntityClick, flyToLocation, sele
|
||||
<div className="map-popup-row">
|
||||
Location: <span className="text-white">{base.country}</span>
|
||||
</div>
|
||||
<div className="mt-1.5 text-[9px] text-red-600 tracking-wider">
|
||||
<div className={`mt-1.5 text-[9px] ${footerCls} tracking-wider`}>
|
||||
MILITARY BASE — {branchLabel[base.branch] || base.branch.toUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -197,6 +197,16 @@ export function buildDataCentersGeoJSON(datacenters?: DataCenter[]): FC {
|
||||
|
||||
// ─── Military Bases ─────────────────────────────────────────────────────────
|
||||
|
||||
// Classify base alignment: red = adversary, blue = US/allied, green = ROC
|
||||
const _ADVERSARY_COUNTRIES = new Set(["China", "Russia", "North Korea"]);
|
||||
const _ROC_COUNTRIES = new Set(["Taiwan"]);
|
||||
|
||||
function _baseSide(country: string, operator: string): "red" | "blue" | "green" {
|
||||
if (_ADVERSARY_COUNTRIES.has(country)) return "red";
|
||||
if (_ROC_COUNTRIES.has(country)) return "green";
|
||||
return "blue";
|
||||
}
|
||||
|
||||
export function buildMilitaryBasesGeoJSON(bases?: MilitaryBase[]): FC {
|
||||
if (!bases?.length) return null;
|
||||
return {
|
||||
@@ -210,6 +220,7 @@ export function buildMilitaryBasesGeoJSON(bases?: MilitaryBase[]): FC {
|
||||
country: base.country || '',
|
||||
operator: base.operator || '',
|
||||
branch: base.branch || '',
|
||||
side: _baseSide(base.country || '', base.operator || ''),
|
||||
},
|
||||
geometry: { type: 'Point' as const, coordinates: [base.lng, base.lat] }
|
||||
}))
|
||||
|
||||
@@ -43,7 +43,7 @@ export interface PrivateJet extends FlightBase {
|
||||
|
||||
export interface MilitaryFlight extends FlightBase {
|
||||
type: "military_flight";
|
||||
military_type?: "heli" | "fighter" | "tanker" | "cargo" | "recon" | "default";
|
||||
military_type?: "heli" | "fighter" | "bomber" | "tanker" | "cargo" | "recon" | "default";
|
||||
force?: string;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,12 @@ export interface Ship {
|
||||
yacht_length?: number;
|
||||
yacht_year?: number;
|
||||
yacht_link?: string;
|
||||
// PLAN/CCG vessel enrichment
|
||||
plan_name?: string;
|
||||
plan_class?: string;
|
||||
plan_force?: string;
|
||||
plan_hull?: string;
|
||||
plan_wiki?: string;
|
||||
// Carrier enrichment
|
||||
wiki?: string;
|
||||
homeport?: string;
|
||||
|
||||
Reference in New Issue
Block a user