fix: 优化虫 机制,新增伊朗支援
This commit is contained in:
@@ -219,9 +219,35 @@ export function WarMap() {
|
||||
const situation = useReplaySituation()
|
||||
const { isReplayMode, playbackTime } = usePlaybackStore()
|
||||
const { usForces, iranForces, conflictEvents = [] } = situation
|
||||
/** 时间衰减基准:回放模式用回放时刻,否则用数据更新时间或当前时间 */
|
||||
const referenceTime =
|
||||
isReplayMode ? playbackTime : situation.lastUpdated || new Date().toISOString()
|
||||
/** 时间衰减基准:回放模式用回放时刻;实时模式用「当前数据中最新 struck_at/attacked_at」,无事件时再用 lastUpdated/now。
|
||||
* 这样爬虫推送后 lastUpdated 跳到「当前」时,窗口仍以数据中最新事件为右端,不会前移导致已有攻击路线被排除(动画消失)。 */
|
||||
const referenceTime = useMemo(() => {
|
||||
if (isReplayMode) return playbackTime
|
||||
let maxTs = 0
|
||||
for (const line of situation.mapData?.strikeLines ?? []) {
|
||||
for (const t of line.targets ?? []) {
|
||||
if (t.struck_at) {
|
||||
const ts = new Date(t.struck_at).getTime()
|
||||
if (ts > maxTs) maxTs = ts
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const loc of [...(usForces.keyLocations ?? []), ...(iranForces.keyLocations ?? [])]) {
|
||||
if (loc.attacked_at) {
|
||||
const ts = new Date(loc.attacked_at).getTime()
|
||||
if (ts > maxTs) maxTs = ts
|
||||
}
|
||||
}
|
||||
if (maxTs > 0) return new Date(maxTs).toISOString()
|
||||
return situation.lastUpdated || new Date().toISOString()
|
||||
}, [
|
||||
isReplayMode,
|
||||
playbackTime,
|
||||
situation.lastUpdated,
|
||||
situation.mapData?.strikeLines,
|
||||
usForces.keyLocations,
|
||||
iranForces.keyLocations,
|
||||
])
|
||||
|
||||
const usLocs = (usForces.keyLocations || []) as KeyLoc[]
|
||||
const irLocs = (iranForces.keyLocations || []) as KeyLoc[]
|
||||
@@ -500,9 +526,33 @@ export function WarMap() {
|
||||
[hormuzTargetPoints, isReplayMode]
|
||||
)
|
||||
|
||||
// 霍尔木兹海峡交战区 & 真主党势力范围(静态面)
|
||||
const hormuzZone = EXTENDED_WAR_ZONES.hormuzCombatZone
|
||||
const hezbollahZone = EXTENDED_WAR_ZONES.hezbollahZone
|
||||
// 霍尔木兹海峡交战区 & 真主党势力范围(静态面);保持引用稳定,避免广播更新时触发 setData 导致轮廓线丢失
|
||||
const hormuzZone = useMemo(() => EXTENDED_WAR_ZONES.hormuzCombatZone, [])
|
||||
const hezbollahZone = useMemo(() => EXTENDED_WAR_ZONES.hezbollahZone, [])
|
||||
const hormuzLabelData = useMemo(
|
||||
() => ({
|
||||
type: 'Feature' as const,
|
||||
properties: { name: (EXTENDED_WAR_ZONES.hormuzCombatZone.properties as { name?: string }).name ?? '霍尔木兹海峡交战区' },
|
||||
geometry: { type: 'Point' as const, coordinates: EXTENDED_WAR_ZONES.hormuzLabelCenter },
|
||||
}),
|
||||
[]
|
||||
)
|
||||
const hezbollahLabelData = useMemo(
|
||||
() => ({
|
||||
type: 'Feature' as const,
|
||||
properties: { name: (EXTENDED_WAR_ZONES.hezbollahZone.properties as { name?: string }).name ?? '真主党势力范围' },
|
||||
geometry: { type: 'Point' as const, coordinates: EXTENDED_WAR_ZONES.hezbollahLabelCenter },
|
||||
}),
|
||||
[]
|
||||
)
|
||||
const kurdishLabelData = useMemo(
|
||||
() => ({
|
||||
type: 'Feature' as const,
|
||||
properties: { name: '库尔德武装' },
|
||||
geometry: { type: 'Point' as const, coordinates: EXTENDED_WAR_ZONES.kurdishLabelCenter },
|
||||
}),
|
||||
[]
|
||||
)
|
||||
|
||||
// GDELT 冲突事件:1–3 绿, 4–6 橙闪, 7–10 红脉
|
||||
const { conflictEventsGreen, conflictEventsOrange, conflictEventsRed } = useMemo(() => {
|
||||
@@ -546,6 +596,8 @@ export function WarMap() {
|
||||
}
|
||||
|
||||
const initAnimation = useRef<(map: MapboxMap) => void>(null!)
|
||||
const [zoneSourceKey, setZoneSourceKey] = useState(0)
|
||||
const lastCheckedZoneRef = useRef<string>('')
|
||||
initAnimation.current = (map: MapboxMap) => {
|
||||
startRef.current = performance.now()
|
||||
|
||||
@@ -837,6 +889,26 @@ export function WarMap() {
|
||||
return () => ro.disconnect()
|
||||
}, [fitToTheater])
|
||||
|
||||
// 广播更新后检查轮廓层是否仍在;若被误删则递进 zoneSourceKey 强制轮廓 Source 重新挂载
|
||||
useEffect(() => {
|
||||
const lastUpdated = situation.lastUpdated ?? ''
|
||||
if (lastUpdated === lastCheckedZoneRef.current) return
|
||||
lastCheckedZoneRef.current = lastUpdated
|
||||
const map = mapRef.current?.getMap()
|
||||
if (!map?.isStyleLoaded()) return
|
||||
const t = setTimeout(() => {
|
||||
const map2 = mapRef.current?.getMap()
|
||||
if (!map2) return
|
||||
const hasKurdish = !!map2.getLayer('kurdish-zones')
|
||||
const hasHormuz = !!map2.getLayer('hormuz-combat-fill')
|
||||
const hasHezbollah = !!map2.getLayer('hezbollah-fill')
|
||||
if (!hasKurdish || !hasHormuz || !hasHezbollah) {
|
||||
setZoneSourceKey((k) => k + 1)
|
||||
}
|
||||
}, 150)
|
||||
return () => clearTimeout(t)
|
||||
}, [situation.lastUpdated])
|
||||
|
||||
if (!MAPBOX_TOKEN) {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center bg-military-dark">
|
||||
@@ -877,6 +949,9 @@ export function WarMap() {
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-1.5 rounded-sm bg-red-500/40" /> 胡塞武装
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-1.5 rounded-sm bg-red-500/40" /> 苏丹武装
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-[#3B82F6]" /> 林肯打击
|
||||
</span>
|
||||
@@ -1241,7 +1316,7 @@ export function WarMap() {
|
||||
</Source>
|
||||
|
||||
{/* 跨国库尔德势力:土(Bakur)/叙(Rojava)/伊(Bashur) 三区 MultiPolygon + 北/南钳形箭头 */}
|
||||
<Source id="kurdish-front-source" type="geojson" data={KURDISH_FRONT_GEOJSON}>
|
||||
<Source key={`zone-kurdish-${zoneSourceKey}`} id="kurdish-front-source" type="geojson" data={KURDISH_FRONT_GEOJSON}>
|
||||
{/* 势力范围:紫色半透明,远景更显、近景更透(描边单独 line 层,参考真主党) */}
|
||||
<Layer
|
||||
id="kurdish-zones"
|
||||
@@ -1686,6 +1761,62 @@ export function WarMap() {
|
||||
/>
|
||||
</Source>
|
||||
|
||||
{/* 苏丹武装势力范围(支持伊朗,伊朗色标轮廓) */}
|
||||
<Source
|
||||
id="sudan-area"
|
||||
type="geojson"
|
||||
data={EXTENDED_WAR_ZONES.sudanZone}
|
||||
>
|
||||
<Layer
|
||||
id="sudan-fill"
|
||||
type="fill"
|
||||
paint={{
|
||||
'fill-color': 'rgba(239, 68, 68, 0.2)',
|
||||
'fill-outline-color': 'transparent',
|
||||
'fill-opacity': 0.4,
|
||||
}}
|
||||
/>
|
||||
</Source>
|
||||
{/* 苏丹国境线(独立线图层,伊朗红) */}
|
||||
<Source
|
||||
id="sudan-border"
|
||||
type="geojson"
|
||||
data={EXTENDED_WAR_ZONES.sudanBorderLine}
|
||||
>
|
||||
<Layer
|
||||
id="sudan-border-line"
|
||||
type="line"
|
||||
paint={{
|
||||
'line-color': '#EF4444',
|
||||
'line-width': 2,
|
||||
'line-opacity': 1,
|
||||
}}
|
||||
/>
|
||||
</Source>
|
||||
<Source
|
||||
id="sudan-label"
|
||||
type="geojson"
|
||||
data={{
|
||||
type: 'Feature',
|
||||
properties: { name: '苏丹武装' },
|
||||
geometry: { type: 'Point', coordinates: EXTENDED_WAR_ZONES.sudanLabelCenter },
|
||||
}}
|
||||
>
|
||||
<Layer
|
||||
id="sudan-label-text"
|
||||
type="symbol"
|
||||
layout={{
|
||||
'text-field': '苏丹武装',
|
||||
'text-size': ['interpolate', ['linear'], ['zoom'], 4, 9, 8, 12],
|
||||
'text-anchor': 'center',
|
||||
}}
|
||||
paint={{
|
||||
'text-color': '#EF4444',
|
||||
'text-halo-width': 0,
|
||||
}}
|
||||
/>
|
||||
</Source>
|
||||
|
||||
{/* 伊朗标注 */}
|
||||
<Source
|
||||
id="iran-label"
|
||||
@@ -1805,7 +1936,7 @@ export function WarMap() {
|
||||
</Source>
|
||||
|
||||
{/* 霍尔木兹海峡交战区 - 金黄色 mesh 区域 */}
|
||||
<Source id="hormuz-combat-zone" type="geojson" data={hormuzZone}>
|
||||
<Source key={`zone-hormuz-${zoneSourceKey}`} id="hormuz-combat-zone" type="geojson" data={hormuzZone}>
|
||||
<Layer
|
||||
id="hormuz-combat-fill"
|
||||
type="fill"
|
||||
@@ -1826,7 +1957,7 @@ export function WarMap() {
|
||||
</Source>
|
||||
|
||||
{/* 真主党势力范围 - 绿色半透明区域 */}
|
||||
<Source id="hezbollah-zone" type="geojson" data={hezbollahZone}>
|
||||
<Source key={`zone-hezbollah-${zoneSourceKey}`} id="hezbollah-zone" type="geojson" data={hezbollahZone}>
|
||||
<Layer
|
||||
id="hezbollah-fill"
|
||||
type="fill"
|
||||
@@ -1846,18 +1977,7 @@ export function WarMap() {
|
||||
</Source>
|
||||
|
||||
{/* 霍尔木兹海峡区域标注 */}
|
||||
<Source
|
||||
id="hormuz-label"
|
||||
type="geojson"
|
||||
data={{
|
||||
type: 'Feature',
|
||||
properties: { name: (hormuzZone.properties as any).name },
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: EXTENDED_WAR_ZONES.hormuzLabelCenter,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Source id="hormuz-label" type="geojson" data={hormuzLabelData}>
|
||||
<Layer
|
||||
id="hormuz-label-text"
|
||||
type="symbol"
|
||||
@@ -1876,18 +1996,7 @@ export function WarMap() {
|
||||
</Source>
|
||||
|
||||
{/* 真主党势力范围标注 */}
|
||||
<Source
|
||||
id="hezbollah-label"
|
||||
type="geojson"
|
||||
data={{
|
||||
type: 'Feature',
|
||||
properties: { name: (hezbollahZone.properties as any).name },
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: EXTENDED_WAR_ZONES.hezbollahLabelCenter,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Source id="hezbollah-label" type="geojson" data={hezbollahLabelData}>
|
||||
<Layer
|
||||
id="hezbollah-label-text"
|
||||
type="symbol"
|
||||
@@ -1906,18 +2015,7 @@ export function WarMap() {
|
||||
</Source>
|
||||
|
||||
{/* 库尔德武装势力范围标注(参照真主党紫色区域标记) */}
|
||||
<Source
|
||||
id="kurdish-label"
|
||||
type="geojson"
|
||||
data={{
|
||||
type: 'Feature',
|
||||
properties: { name: '库尔德武装' },
|
||||
geometry: {
|
||||
type: 'Point',
|
||||
coordinates: EXTENDED_WAR_ZONES.kurdishLabelCenter,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Source id="kurdish-label" type="geojson" data={kurdishLabelData}>
|
||||
<Layer
|
||||
id="kurdish-label-text"
|
||||
type="symbol"
|
||||
|
||||
@@ -91,6 +91,76 @@ export const EXTENDED_WAR_ZONES = {
|
||||
// 真主党区域标注点(用于显示文字)
|
||||
hezbollahLabelCenter: [35.7, 33.7] as [number, number],
|
||||
|
||||
// 苏丹武装势力范围(公开支持伊朗,轮廓用伊朗色标绘制整个苏丹国家)
|
||||
sudanZone: {
|
||||
type: 'Feature' as const,
|
||||
properties: {
|
||||
name: '苏丹武装',
|
||||
status: 'IRAN-ALIGNED',
|
||||
color: '#EF4444',
|
||||
},
|
||||
geometry: {
|
||||
type: 'Polygon' as const,
|
||||
coordinates: [
|
||||
[
|
||||
[31.0, 22.0],
|
||||
[33.0, 22.0],
|
||||
[34.5, 22.0],
|
||||
[36.8, 22.0],
|
||||
[37.3, 20.8],
|
||||
[38.5, 18.0],
|
||||
[37.9, 17.0],
|
||||
[36.5, 14.3],
|
||||
[35.0, 13.5],
|
||||
[34.0, 11.5],
|
||||
[32.5, 12.0],
|
||||
[29.5, 9.5],
|
||||
[27.0, 9.0],
|
||||
[24.0, 8.5],
|
||||
[23.5, 10.0],
|
||||
[22.5, 13.5],
|
||||
[22.5, 16.0],
|
||||
[24.0, 20.0],
|
||||
[25.0, 22.0],
|
||||
[31.0, 22.0],
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
sudanLabelCenter: [30.5, 15.25] as [number, number],
|
||||
|
||||
/** 苏丹国境线(与 sudanZone 同轮廓,LineString 供线图层绘制) */
|
||||
sudanBorderLine: {
|
||||
type: 'Feature' as const,
|
||||
properties: { name: '苏丹国境线' },
|
||||
geometry: {
|
||||
type: 'LineString' as const,
|
||||
coordinates: [
|
||||
[31.0, 22.0],
|
||||
[33.0, 22.0],
|
||||
[34.5, 22.0],
|
||||
[36.8, 22.0],
|
||||
[37.3, 20.8],
|
||||
[38.5, 18.0],
|
||||
[37.9, 17.0],
|
||||
[36.5, 14.3],
|
||||
[35.0, 13.5],
|
||||
[34.0, 11.5],
|
||||
[32.5, 12.0],
|
||||
[29.5, 9.5],
|
||||
[27.0, 9.0],
|
||||
[24.0, 8.5],
|
||||
[23.5, 10.0],
|
||||
[22.5, 13.5],
|
||||
[22.5, 16.0],
|
||||
[24.0, 20.0],
|
||||
[25.0, 22.0],
|
||||
[31.0, 22.0],
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// 库尔德武装势力区域标注点(叙/土/伊三区紫色带中心附近)
|
||||
kurdishLabelCenter: [43.5, 36.3] as [number, number],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user