GenerAI / worldmonitor /shared /china-logistics-corridors.ts
amogaddy's picture
Integra World Monitor (AGPL-3.0, self-hosted) nello Space: pagina, menu, e arricchimento notizie per la AI (part 6)
fa9c65f verified
Raw
History Blame Contribute Delete
13.4 kB
export const CHINA_CORRIDOR_SIGNAL_FAMILIES = [
'port',
'aviation',
'hazard',
'power_energy',
'strategic_industry',
'trade',
] as const;
export type ChinaCorridorSignalFamily = (typeof CHINA_CORRIDOR_SIGNAL_FAMILIES)[number];
export const CHINA_LOGISTICS_CORRIDOR_IDS = [
'china-yangtze-river-delta',
'china-greater-bay-area',
'china-bohai-rim',
'china-western-land-sea-corridor',
] as const;
export type ChinaLogisticsCorridorId = (typeof CHINA_LOGISTICS_CORRIDOR_IDS)[number];
export const CHINA_CORRIDOR_NODE_TYPES = [
'port',
'airport',
'crossing',
'industrial',
] as const;
export type ChinaCorridorNodeType = (typeof CHINA_CORRIDOR_NODE_TYPES)[number];
export interface ChinaCorridorPoint {
lat: number;
lon: number;
}
export interface ChinaCorridorNode extends ChinaCorridorPoint {
id: string;
name: string;
type: ChinaCorridorNodeType;
sourceOwner: string;
sourceSelector?: Readonly<{
family: ChinaCorridorSignalFamily;
id: string;
}>;
}
export interface ChinaCorridorSourceSelector {
family: ChinaCorridorSignalFamily;
id: string;
}
export interface ChinaLogisticsCorridor {
id: ChinaLogisticsCorridorId;
name: string;
description: string;
boundary: readonly ChinaCorridorPoint[];
nodes: readonly ChinaCorridorNode[];
signalFamilies: readonly ChinaCorridorSignalFamily[];
sourceSelectors: readonly ChinaCorridorSourceSelector[];
}
const allSignalFamilies = [...CHINA_CORRIDOR_SIGNAL_FAMILIES] as const;
function node(
id: string,
name: string,
type: ChinaCorridorNodeType,
lat: number,
lon: number,
sourceOwner: string,
sourceSelector?: ChinaCorridorNode['sourceSelector'],
): ChinaCorridorNode {
return { id: `china-node:${id}`, name, type, lat, lon, sourceOwner, sourceSelector };
}
function selectorsFromNodes(nodes: readonly ChinaCorridorNode[]): ChinaCorridorSourceSelector[] {
return nodes.flatMap((item) => item.sourceSelector ? [item.sourceSelector] : []);
}
function corridor(
definition: Omit<ChinaLogisticsCorridor, 'signalFamilies' | 'sourceSelectors'> & {
sourceSelectors?: readonly ChinaCorridorSourceSelector[];
},
): ChinaLogisticsCorridor {
const sourceSelectors = [
...selectorsFromNodes(definition.nodes),
...(definition.sourceSelectors ?? []),
];
return Object.freeze({
...definition,
signalFamilies: allSignalFamilies,
sourceSelectors: Object.freeze(sourceSelectors),
});
}
const yrdNodes = [
node('yrd-port-shanghai', 'Shanghai', 'port', 31.1918, 121.6442, 'IMF PortWatch', { family: 'port', id: 'port1188' }),
node('yrd-port-yangshan', 'Shanghai Yangshan', 'port', 30.6151, 122.0748, 'IMF PortWatch', { family: 'port', id: 'port2027' }),
node('yrd-port-ningbo', 'Ningbo', 'port', 29.9208, 121.8818, 'IMF PortWatch', { family: 'port', id: 'port824' }),
node('yrd-port-zhoushan', 'Zhoushan', 'port', 30.0564, 122.1008, 'IMF PortWatch', { family: 'port', id: 'port1429' }),
node('yrd-port-taicang', 'Suzhou Taicang', 'port', 31.65, 121.2, 'IMF PortWatch', { family: 'port', id: 'port1253' }),
node('yrd-port-nanjing', 'Nanjing', 'port', 32.08, 118.77, 'IMF PortWatch', { family: 'port', id: 'port2020' }),
node('yrd-airport-pvg', 'Shanghai Pudong International', 'airport', 31.1443, 121.8083, 'AviationStack', { family: 'aviation', id: 'PVG' }),
node('yrd-industry-shanghai', 'Shanghai advanced manufacturing cluster', 'industrial', 31.2304, 121.4737, 'Reviewed WorldMonitor configuration'),
node('yrd-industry-suzhou', 'Suzhou industrial cluster', 'industrial', 31.2989, 120.5853, 'Reviewed WorldMonitor configuration'),
node('yrd-industry-ningbo', 'Ningbo-Zhoushan industrial cluster', 'industrial', 29.8683, 121.5440, 'Reviewed WorldMonitor configuration'),
] as const;
const gbaNodes = [
node('gba-port-shekou', 'Shekou Shenzhen', 'port', 22.47, 113.90, 'IMF PortWatch', { family: 'port', id: 'port1189' }),
node('gba-port-yantian', 'Yantian', 'port', 22.58, 114.27, 'IMF PortWatch', { family: 'port', id: 'port1414' }),
node('gba-port-mawan', 'Mawan', 'port', 22.49, 113.87, 'IMF PortWatch', { family: 'port', id: 'port2028' }),
node('gba-port-dachan-bay', 'Dachan Bay', 'port', 22.55, 113.80, 'IMF PortWatch', { family: 'port', id: 'port2029' }),
node('gba-port-qianhai-bay', 'Qianhai Bay', 'port', 22.53, 113.89, 'IMF PortWatch', { family: 'port', id: 'port2030' }),
node('gba-port-nansha', 'Guangzhou Nansha', 'port', 22.63, 113.67, 'IMF PortWatch', { family: 'port', id: 'port425' }),
node('gba-port-huangpu', 'Guangzhou Huangpu', 'port', 23.10, 113.46, 'IMF PortWatch', { family: 'port', id: 'port2401' }),
node('gba-port-zhuhai', 'Zhuhai', 'port', 22.27, 113.58, 'IMF PortWatch', { family: 'port', id: 'port2112' }),
node('gba-port-hong-kong', 'Hong Kong', 'port', 22.3351, 114.0967, 'IMF PortWatch', { family: 'port', id: 'port474' }),
node('gba-airport-can', 'Guangzhou Baiyun International', 'airport', 23.3924, 113.2988, 'AviationStack', { family: 'aviation', id: 'CAN' }),
node('gba-airport-szx', 'Shenzhen Bao’an International', 'airport', 22.6393, 113.8107, 'AviationStack', { family: 'aviation', id: 'SZX' }),
node('gba-airport-hkg', 'Hong Kong International', 'airport', 22.3080, 113.9185, 'AviationStack', { family: 'aviation', id: 'HKG' }),
node('gba-industry-shenzhen', 'Shenzhen technology manufacturing cluster', 'industrial', 22.5431, 114.0579, 'Reviewed WorldMonitor configuration'),
node('gba-industry-guangzhou', 'Guangzhou-Foshan industrial cluster', 'industrial', 23.1291, 113.2644, 'Reviewed WorldMonitor configuration'),
node('gba-industry-hong-kong', 'Hong Kong trade and finance gateway', 'industrial', 22.3193, 114.1694, 'Reviewed WorldMonitor configuration'),
] as const;
const bohaiNodes = [
node('bohai-port-qingdao', 'Qingdao', 'port', 36.07, 120.32, 'IMF PortWatch', { family: 'port', id: 'port1069' }),
node('bohai-port-tianjin', 'Tianjin Xin Gang', 'port', 38.98, 117.75, 'IMF PortWatch', { family: 'port', id: 'port1297' }),
node('bohai-port-qinhuangdao', 'Qinhuangdao', 'port', 39.91, 119.60, 'IMF PortWatch', { family: 'port', id: 'port1072' }),
node('bohai-port-tangshan', 'Tangshan Jingtang', 'port', 39.22, 119.00, 'IMF PortWatch', { family: 'port', id: 'port1266' }),
node('bohai-port-shougang', 'Shougang Jingtang', 'port', 39.20, 119.00, 'IMF PortWatch', { family: 'port', id: 'port1195' }),
node('bohai-port-dalian', 'Dalian', 'port', 38.92, 121.64, 'IMF PortWatch', { family: 'port', id: 'port273' }),
node('bohai-port-huanghua', 'Huanghua', 'port', 38.32, 117.87, 'IMF PortWatch', { family: 'port', id: 'port154' }),
node('bohai-airport-pek', 'Beijing Capital International', 'airport', 40.0799, 116.6031, 'AviationStack', { family: 'aviation', id: 'PEK' }),
node('bohai-industry-beijing-tianjin', 'Beijing-Tianjin industrial cluster', 'industrial', 39.50, 117.20, 'Reviewed WorldMonitor configuration'),
node('bohai-industry-shandong', 'Shandong manufacturing cluster', 'industrial', 36.6512, 117.1201, 'Reviewed WorldMonitor configuration'),
node('bohai-industry-liaoning', 'Liaoning heavy-industry cluster', 'industrial', 41.8057, 123.4315, 'Reviewed WorldMonitor configuration'),
] as const;
const westernNodes = [
node('western-port-qinzhou', 'Qinzhou', 'port', 21.6788, 108.6383, 'IMF PortWatch', { family: 'port', id: 'port1071' }),
node('western-port-fangcheng', 'Fangcheng', 'port', 21.6142, 108.3643, 'IMF PortWatch', { family: 'port', id: 'port339' }),
node('western-port-beibu-gulf', 'Guangxi Beibu Gulf Port', 'port', 21.5109, 109.5385, 'IMF PortWatch', { family: 'port', id: 'port424' }),
node('western-airport-ctu', 'Chengdu Shuangliu International', 'airport', 30.5785, 103.9471, 'AviationStack', { family: 'aviation', id: 'CTU' }),
node('western-airport-kmg', 'Kunming Changshui International', 'airport', 25.1019, 102.9292, 'AviationStack', { family: 'aviation', id: 'KMG' }),
node('western-airport-urc', 'Ürümqi Diwopu International', 'airport', 43.9071, 87.4742, 'AviationStack', { family: 'aviation', id: 'URC' }),
node('western-crossing-khorgos', 'Khorgos land crossing', 'crossing', 44.2140, 80.4100, 'Reviewed WorldMonitor configuration'),
node('western-crossing-alashankou', 'Alashankou land crossing', 'crossing', 45.1700, 82.5700, 'Reviewed WorldMonitor configuration'),
node('western-crossing-pingxiang', 'Pingxiang land crossing', 'crossing', 22.1080, 106.7560, 'Reviewed WorldMonitor configuration'),
node('western-industry-chengdu', 'Chengdu manufacturing cluster', 'industrial', 30.5728, 104.0668, 'Reviewed WorldMonitor configuration'),
node('western-industry-chongqing', 'Chongqing manufacturing cluster', 'industrial', 29.5630, 106.5516, 'Reviewed WorldMonitor configuration'),
node('western-industry-urumqi', 'Ürümqi western logistics hub', 'industrial', 43.8256, 87.6168, 'Reviewed WorldMonitor configuration'),
] as const;
export const CHINA_LOGISTICS_CORRIDORS: readonly ChinaLogisticsCorridor[] = Object.freeze([
corridor({
id: 'china-yangtze-river-delta',
name: 'Yangtze River Delta',
description: 'Reviewed coastal logistics corridor joining Shanghai, Jiangsu, and Zhejiang port, aviation, and advanced-manufacturing nodes.',
boundary: Object.freeze([
{ lat: 28.4, lon: 118.2 },
{ lat: 28.4, lon: 123.2 },
{ lat: 33.4, lon: 123.2 },
{ lat: 33.4, lon: 118.2 },
{ lat: 28.4, lon: 118.2 },
]),
nodes: yrdNodes,
}),
corridor({
id: 'china-greater-bay-area',
name: 'Greater Bay Area',
description: 'Reviewed Pearl River Delta corridor connecting Guangdong, Hong Kong, and Macao-facing port, airport, and industrial gateways.',
boundary: Object.freeze([
{ lat: 20.5, lon: 111.5 },
{ lat: 20.5, lon: 115.8 },
{ lat: 24.8, lon: 115.8 },
{ lat: 24.8, lon: 111.5 },
{ lat: 20.5, lon: 111.5 },
]),
nodes: gbaNodes,
sourceSelectors: [{ family: 'hazard', id: 'hazard:hko-warnings' }],
}),
corridor({
id: 'china-bohai-rim',
name: 'Bohai Rim',
description: 'Reviewed northern maritime-industrial corridor spanning Beijing-Tianjin, Hebei, Shandong, Liaoning, and their major gateways.',
boundary: Object.freeze([
{ lat: 35.5, lon: 116.0 },
{ lat: 35.5, lon: 123.5 },
{ lat: 42.2, lon: 123.5 },
{ lat: 42.2, lon: 116.0 },
{ lat: 35.5, lon: 116.0 },
]),
nodes: bohaiNodes,
}),
corridor({
id: 'china-western-land-sea-corridor',
name: 'Western Land-Sea Corridor',
description: 'Reviewed western corridor linking inland manufacturing hubs, Xinjiang land crossings, and Guangxi gateways to regional and maritime routes.',
boundary: Object.freeze([
{ lat: 20.4, lon: 106.2 },
{ lat: 20.4, lon: 110.4 },
{ lat: 32.5, lon: 110.4 },
{ lat: 47.0, lon: 91.0 },
{ lat: 47.0, lon: 78.5 },
{ lat: 41.0, lon: 78.5 },
{ lat: 27.0, lon: 99.7 },
{ lat: 22.0, lon: 105.8 },
{ lat: 20.4, lon: 106.2 },
]),
nodes: westernNodes,
}),
]);
const NATIONAL_SOURCE_SELECTORS: readonly ChinaCorridorSourceSelector[] = Object.freeze([
{ family: 'power_energy', id: 'energy:spine:v1:CN' },
{ family: 'strategic_industry', id: 'comtrade:reporter:156:strategic-products' },
{ family: 'trade', id: 'comtrade:reporter:156' },
{ family: 'trade', id: 'supply_chain:shipping:v2:CCFI' },
]);
export function findCorridorsForSourceSelector(
family: ChinaCorridorSignalFamily,
selectorId: string,
): ChinaLogisticsCorridorId[] {
if (NATIONAL_SOURCE_SELECTORS.some((selector) =>
selector.family === family && selector.id === selectorId)) {
return [...CHINA_LOGISTICS_CORRIDOR_IDS];
}
return CHINA_LOGISTICS_CORRIDORS
.filter((item) => item.sourceSelectors.some((selector) =>
selector.family === family && selector.id === selectorId))
.map((item) => item.id);
}
function pointOnSegment(point: ChinaCorridorPoint, a: ChinaCorridorPoint, b: ChinaCorridorPoint): boolean {
if (a.lat === b.lat && a.lon === b.lon) {
return point.lat === a.lat && point.lon === a.lon;
}
const cross = (point.lon - a.lon) * (b.lat - a.lat) - (point.lat - a.lat) * (b.lon - a.lon);
if (Math.abs(cross) > 1e-9) return false;
const dot = (point.lon - a.lon) * (b.lon - a.lon) + (point.lat - a.lat) * (b.lat - a.lat);
if (dot < 0) return false;
const squaredLength = (b.lon - a.lon) ** 2 + (b.lat - a.lat) ** 2;
return dot <= squaredLength;
}
function pointInPolygon(point: ChinaCorridorPoint, polygon: readonly ChinaCorridorPoint[]): boolean {
let inside = false;
for (let index = 0, previous = polygon.length - 1; index < polygon.length; previous = index++) {
const a = polygon[index];
const b = polygon[previous];
if (!a || !b) continue;
if (pointOnSegment(point, a, b)) return true;
const intersects = (a.lat > point.lat) !== (b.lat > point.lat)
&& point.lon < ((b.lon - a.lon) * (point.lat - a.lat)) / (b.lat - a.lat) + a.lon;
if (intersects) inside = !inside;
}
return inside;
}
export function resolveCorridorForPoint(point: ChinaCorridorPoint): ChinaLogisticsCorridorId | null {
if (!Number.isFinite(point.lat) || !Number.isFinite(point.lon)) return null;
const matches = CHINA_LOGISTICS_CORRIDORS.filter((item) => pointInPolygon(point, item.boundary));
return matches.length === 1 ? matches[0]?.id ?? null : null;
}