Files
frontend-miniapp/scripts/check-sgs-hall-poi-adapter.mjs

180 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import assert from 'node:assert/strict'
const exhibitionSpaceTypeWhitelist = new Set([
'exhibition_hall',
'theater',
'education_activity',
'ramp'
])
const exhibitionSpaceKeywords = [
'展厅',
'临展',
'展览',
'影院',
'球幕',
'巨幕',
'报告厅',
'活动室',
'科普',
'实验室',
'展览坡道',
'宇宙',
'地球',
'演化',
'恐龙',
'人类',
'生物',
'生态',
'家园'
]
const normalizedText = (value) => (value || '').trim()
const normalizedHallName = (value) => normalizedText(value).replace(/[\s()【】\[\]_-]/g, '')
const hasExhibitionKeyword = (value) => exhibitionSpaceKeywords.some((keyword) => normalizedText(value).includes(keyword))
const isEligiblePublicExhibitionSpace = (space) => {
const type = normalizedText(space.type).toLowerCase()
const name = normalizedText(space.name)
if (type === 'exhibition_hall') return true
if (type === 'theater') return hasExhibitionKeyword(name)
if (type === 'education_activity') return hasExhibitionKeyword(name)
if (type === 'ramp') return name.includes('展览坡道')
return false
}
const placeHallName = (place) => (
normalizedText(place.ownerName)
|| normalizedText(place.name)
.replace(/(?:主)?(?:出入口|入口|出口|门)\s*[A-Za-z0-9一二三四五六七八九十号-]*$/u, '')
.trim()
|| normalizedText(place.name)
)
const isExhibitionHallNavigablePlace = (place) => {
const searchableText = [
place.name,
place.ownerName,
place.category,
place.type,
place.typeCode,
place.typeName
]
.map(normalizedText)
.filter(Boolean)
.join(' ')
return hasExhibitionKeyword(searchableText)
}
const findMatchedHallSpace = (place, spaces) => {
const placeName = normalizedHallName(placeHallName(place))
if (!placeName) return undefined
return spaces.find((space) => {
const spaceName = normalizedHallName(space.name)
return spaceName && (placeName.includes(spaceName) || spaceName.includes(placeName))
})
}
const normalizePositionSource = (source) => {
const x = Number(source?.x)
const y = Number(source?.y)
const z = Number(source?.z)
if (![x, y, z].every(Number.isFinite)) return undefined
return [x, y, z]
}
const normalizePlacePosition = (place) => normalizePositionSource(
place.position
? { x: place.position.x, y: place.position.y ?? 0, z: place.position.z }
: { x: place.x, y: place.y ?? 0, z: place.z }
)
const normalizeSpaceCenter = (space) => normalizePositionSource(space.center || {})
const createHallPoiId = (space, place, fallbackIndex) => {
if (space?.id) return `hall-${space.id}`
const ownerName = normalizedHallName(placeHallName(place))
if (ownerName) return `hall-place-${ownerName}`
return `hall-place-${place.id || fallbackIndex + 1}`
}
const toHallPois = (spaces, navigablePlaces, fallbackFloorId) => {
const hallSpaces = spaces.filter(isEligiblePublicExhibitionSpace)
const hallPlaces = navigablePlaces.filter(isExhibitionHallNavigablePlace)
const halls = new Map()
hallPlaces.forEach((place, index) => {
const matchedSpace = findMatchedHallSpace(place, hallSpaces)
const entrancePosition = normalizePlacePosition(place)
const fallbackPosition = matchedSpace ? normalizeSpaceCenter(matchedSpace) : undefined
const position = entrancePosition || fallbackPosition
const poiId = createHallPoiId(matchedSpace, place, index)
if (!position) return
const existing = halls.get(poiId)
if (existing) {
existing.entrances.push({ name: place.name, positionGltf: entrancePosition })
return
}
halls.set(poiId, {
id: poiId,
name: matchedSpace?.name || placeHallName(place),
floorId: String(place.floorId || matchedSpace?.floorId || fallbackFloorId),
primaryCategory: 'exhibition_hall',
positionGltf: position,
sourceConfidence: entrancePosition ? 'backend-sgs-sdk-hall-entrance' : 'backend-sgs-sdk-space-center',
entrances: [{ name: place.name, positionGltf: entrancePosition }]
})
})
hallSpaces.forEach((space, index) => {
const poiId = `hall-${space.id}`
if (halls.has(poiId)) return
const position = normalizeSpaceCenter(space)
if (!position) return
halls.set(poiId, {
id: poiId,
name: space.name,
floorId: String(space.floorId || fallbackFloorId),
primaryCategory: 'exhibition_hall',
positionGltf: position,
sourceConfidence: 'backend-sgs-sdk-space-center',
entrances: []
})
})
return Array.from(halls.values())
}
const spaces = [
{ id: 1, name: '展厅1宇宙厅', type: 'exhibition_hall', floorId: 'B2', center: { x: 10, y: 0, z: 20 } },
{ id: 2, name: '巨幕影院', type: 'theater', floorId: 'L1', center: { x: 30, y: 0, z: 40 } },
{ id: 3, name: '博物馆之友活动室', type: 'education_activity', floorId: 'L4', center: { x: 50, y: 0, z: 60 } },
{ id: 4, name: '展览坡道', type: 'ramp', floorId: 'L1.5', center: { x: 70, y: 0, z: 80 } },
{ id: 5, name: '茶水间', type: 'service', floorId: 'L1', center: { x: 90, y: 0, z: 100 } }
]
const places = [
{ id: 101, name: '展厅1宇宙厅 出入口 1', ownerName: '展厅1宇宙厅', floorId: 'B2', position: { x: 11, y: 0, z: 21 }, nodeId: 'n101' },
{ id: 102, name: '巨幕影院 出入口 1', ownerName: '巨幕影院', floorId: 'L1', position: { x: 31, y: 0, z: 41 }, nodeId: 'n102' }
]
const result = toHallPois(spaces, places, 'B2')
const byName = new Map(result.map((poi) => [poi.name, poi]))
assert.equal(result.length, 4)
assert.deepEqual(byName.get('展厅1宇宙厅').positionGltf, [11, 0, 21])
assert.deepEqual(byName.get('巨幕影院').positionGltf, [31, 0, 41])
assert.deepEqual(byName.get('博物馆之友活动室').positionGltf, [50, 0, 60])
assert.deepEqual(byName.get('展览坡道').positionGltf, [70, 0, 80])
assert.equal(byName.has('茶水间'), false)
assert.equal(byName.get('博物馆之友活动室').sourceConfidence, 'backend-sgs-sdk-space-center')
console.log('SGS hall POI adapter smoke check passed')