修复导览与讲解位置预览逻辑
This commit is contained in:
@@ -36,12 +36,15 @@ import {
|
||||
toGuideMapDiagnostics,
|
||||
toLocationPreviewFromPoi,
|
||||
toMuseumFloorFromSgs,
|
||||
toMuseumHallPoisFromSgs,
|
||||
toMuseumPoiFromSgs,
|
||||
toRouteReadinessFromSgsDiagnostics
|
||||
} from '@/data/adapters/sgsSdkGuideAdapter'
|
||||
|
||||
const searchableCategories = new Set([
|
||||
'touring_poi',
|
||||
'exhibition_hall',
|
||||
'exhibition_hall_entrance',
|
||||
'basic_service_facility',
|
||||
'transport_circulation',
|
||||
'accessibility_special_service',
|
||||
@@ -50,6 +53,7 @@ const searchableCategories = new Set([
|
||||
|
||||
const toSearchText = (poi: MuseumPoi) => [
|
||||
poi.name,
|
||||
poi.hallName,
|
||||
poi.floorId,
|
||||
poi.floorLabel,
|
||||
poi.primaryCategory.label,
|
||||
@@ -71,7 +75,11 @@ const toLocationPreview = (poi: MuseumPoi): GuideLocationPreview => ({
|
||||
floorLabel: poi.floorLabel,
|
||||
primaryCategoryZh: poi.primaryCategory.label,
|
||||
positionGltf: poi.positionGltf,
|
||||
sourceObjectName: poi.sourceObjectName
|
||||
sourceObjectName: poi.sourceObjectName,
|
||||
kind: poi.kind,
|
||||
hallId: poi.hallId,
|
||||
hallName: poi.hallName,
|
||||
entrances: poi.entrances
|
||||
})
|
||||
|
||||
const isSearchableIndoorPoi = (poi: MuseumPoi) => (
|
||||
@@ -79,6 +87,15 @@ const isSearchableIndoorPoi = (poi: MuseumPoi) => (
|
||||
&& isPoiOnIndoorNavigableFloor(poi)
|
||||
)
|
||||
|
||||
const dedupePoisById = (pois: MuseumPoi[]) => {
|
||||
const seen = new Set<string>()
|
||||
return pois.filter((poi) => {
|
||||
if (!poi.id || seen.has(poi.id)) return false
|
||||
seen.add(poi.id)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
export interface GuideRepository {
|
||||
getAssetBaseUrl(): string
|
||||
getFloors(): Promise<MuseumFloor[]>
|
||||
@@ -245,16 +262,38 @@ export class SgsSdkGuideRepository implements GuideRepository {
|
||||
const indoorFloors = manifest.floors.filter(isIndoorNavigableFloor)
|
||||
const indoorFloorIds = new Set(indoorFloors.map((floor) => String(floor.floorId)))
|
||||
this.floorAliases = buildSgsFloorAliases(indoorFloors)
|
||||
const poiGroups = await Promise.all(
|
||||
indoorFloors.map((floor) => this.provider.getFloorPois(String(floor.floorId)))
|
||||
const floorDataGroups = await Promise.all(
|
||||
indoorFloors.map(async (floor) => {
|
||||
const floorId = String(floor.floorId)
|
||||
const [pois, spaces, navigablePlaces] = await Promise.all([
|
||||
this.provider.getFloorPois(floorId).catch(() => []),
|
||||
this.provider.getFloorSpaces(floorId).catch(() => []),
|
||||
this.provider.getNavigablePlaces(floorId).catch(() => [])
|
||||
])
|
||||
|
||||
return {
|
||||
floorId,
|
||||
pois,
|
||||
hallPois: toMuseumHallPoisFromSgs(spaces, navigablePlaces, manifest.floors, floorId)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
this.poiCache = poiGroups
|
||||
.flat()
|
||||
const ordinaryPois = floorDataGroups
|
||||
.flatMap((group) => group.pois)
|
||||
.map((poi) => toMuseumPoiFromSgs(poi, manifest.floors))
|
||||
.filter((poi) => poi.id && poi.name)
|
||||
.filter((poi) => indoorFloorIds.has(String(poi.floorId)) || isPoiOnIndoorNavigableFloor(poi))
|
||||
|
||||
const hallPois = floorDataGroups
|
||||
.flatMap((group) => group.hallPois)
|
||||
.filter((poi) => indoorFloorIds.has(String(poi.floorId)) || isPoiOnIndoorNavigableFloor(poi))
|
||||
|
||||
this.poiCache = dedupePoisById([
|
||||
...hallPois,
|
||||
...ordinaryPois
|
||||
])
|
||||
|
||||
return this.poiCache
|
||||
}
|
||||
|
||||
|
||||
@@ -136,6 +136,10 @@ const createUnavailableReadiness = (message: string): GuideRouteReadiness => ({
|
||||
requiredData: ['route_graph', 'nav_data']
|
||||
})
|
||||
|
||||
const logRouteReadinessIssue = (message: string, payload: Record<string, unknown>) => {
|
||||
console.error(`[RouteReadiness] ${message}`, payload)
|
||||
}
|
||||
|
||||
export class StaticGuideRouteRepository implements GuideRouteRepository {
|
||||
private datasetCache: NavRouteDataset | null = null
|
||||
private datasetRequest: Promise<NavRouteDataset> | null = null
|
||||
@@ -156,11 +160,17 @@ export class StaticGuideRouteRepository implements GuideRouteRepository {
|
||||
try {
|
||||
const dataset = await this.loadDataset()
|
||||
if (!dataset.nodes.size || !dataset.edges.length || !dataset.anchorsByPoiId.size) {
|
||||
return createUnavailableReadiness('路线示意数据不完整,当前暂不支持正式导航')
|
||||
logRouteReadinessIssue('路线数据不完整', {
|
||||
nodes: dataset.nodes.size,
|
||||
edges: dataset.edges.length,
|
||||
anchorsByPoiId: dataset.anchorsByPoiId.size
|
||||
})
|
||||
return createUnavailableReadiness('无法定位,当前仅支持点位位置预览')
|
||||
}
|
||||
|
||||
return applyRouteReadinessGate(manifestReadiness)
|
||||
} catch (error) {
|
||||
console.error('[RouteReadiness] 静态路线数据加载失败', error)
|
||||
return createUnavailableReadiness(error instanceof Error ? error.message : '路线示意数据加载失败')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
toGuideRouteResultFromSgsPlan
|
||||
} from '@/data/adapters/sgsSdkRouteAdapter'
|
||||
import { stringifyId, formatSgsFloorLabel } from '@/data/adapters/sgsSdkGuideAdapter'
|
||||
import { toAppFloorId } from '@/services/sgs/SgsMapEventAdapter'
|
||||
|
||||
const createUnavailableReadiness = (message: string): GuideRouteReadiness => ({
|
||||
ready: false,
|
||||
@@ -29,6 +28,10 @@ const createUnavailableReadiness = (message: string): GuideRouteReadiness => ({
|
||||
requiredData: ['route_graph', 'nav_data']
|
||||
})
|
||||
|
||||
const logSdkRouteReadinessIssue = (message: string, payload: Record<string, unknown>) => {
|
||||
console.error(`[SDKRouteReadiness] ${message}`, payload)
|
||||
}
|
||||
|
||||
const toNumber = (value: unknown) => (
|
||||
Number.isFinite(Number(value)) ? Number(value) : undefined
|
||||
)
|
||||
@@ -51,11 +54,6 @@ const positionFromPlace = (place: SgsNavigablePlacePayload): [number, number, nu
|
||||
]
|
||||
}
|
||||
|
||||
const numericId = (value: unknown) => {
|
||||
const numberValue = Number(value)
|
||||
return Number.isFinite(numberValue) ? numberValue : undefined
|
||||
}
|
||||
|
||||
export class SdkGuideRouteRepository {
|
||||
private targetsCache: GuideRouteTarget[] | null = null
|
||||
|
||||
@@ -84,7 +82,19 @@ export class SdkGuideRouteRepository {
|
||||
|
||||
// 没有楼层有路线数据时不可用
|
||||
if (readyFloors.length === 0) {
|
||||
return createUnavailableReadiness('SDK 路线规划数据不完整,当前暂不支持正式导航')
|
||||
logSdkRouteReadinessIssue('SDK 路线数据不完整', {
|
||||
floorCount: floors.length,
|
||||
readyFloorCount: readyFloors.length,
|
||||
floors: floors.map((floor) => ({
|
||||
floorId: floor.floorId,
|
||||
floorName: floor.floorName,
|
||||
floorCode: floor.floorCode,
|
||||
routePlanningReady: floor.routePlanningReady,
|
||||
routeNodeCount: floor.routeNodeCount,
|
||||
routeEdgeCount: floor.routeEdgeCount
|
||||
}))
|
||||
})
|
||||
return createUnavailableReadiness('无法定位,当前仅支持点位位置预览')
|
||||
}
|
||||
|
||||
// 收集未就绪的楼层
|
||||
@@ -97,19 +107,20 @@ export class SdkGuideRouteRepository {
|
||||
.join('、')
|
||||
return {
|
||||
ready: true,
|
||||
message: `部分楼层路线数据未就绪:${floorLabels},已就绪楼层可正常使用导览功能`,
|
||||
message: `部分楼层位置关系数据未就绪:${floorLabels},可查看已就绪楼层的位置关系`,
|
||||
requiredData: []
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ready: true,
|
||||
message: 'SDK 路线数据已就绪',
|
||||
message: 'SDK 位置关系数据已接入,可进行位置预览',
|
||||
requiredData: []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[SDKRouteReadiness] 读取 SDK 路线状态失败', error)
|
||||
return createUnavailableReadiness(
|
||||
error instanceof Error ? error.message : 'SDK 路线数据加载失败'
|
||||
error instanceof Error ? error.message : 'SDK 位置关系数据加载失败'
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -148,8 +159,8 @@ export class SdkGuideRouteRepository {
|
||||
targets.push({
|
||||
poiId: placeId,
|
||||
name: placeName,
|
||||
// 展示用标准化的 floorId(如 L1)
|
||||
floorId: toAppFloorId(place.floorId ?? rawFloorId),
|
||||
// 保留后端原始 floorId,确保与 SDK 模型楼层和 ThreeMap 当前楼层一致。
|
||||
floorId: stringifyId(place.floorId ?? rawFloorId),
|
||||
floorLabel,
|
||||
categoryLabel,
|
||||
positionGltf,
|
||||
@@ -188,7 +199,7 @@ export class SdkGuideRouteRepository {
|
||||
const endTarget = targets.find((target) => target.poiId === endPoiId)
|
||||
|
||||
if (!startTarget || !endTarget) {
|
||||
throw new GuideRouteError('ROUTE_TARGET_MISSING', '起点或终点不在 SDK 可导航目的地列表中')
|
||||
throw new GuideRouteError('ROUTE_TARGET_MISSING', '起点或终点不在 SDK 可预览目的地列表中')
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -196,24 +207,15 @@ export class SdkGuideRouteRepository {
|
||||
throw new GuideRouteError('ROUTE_TARGET_UNANCHORED', '起点或终点缺少可规划坐标')
|
||||
}
|
||||
|
||||
// 获取原始 floorId 用于 API 调用
|
||||
const diagnostics = await this.sdkApiProvider.getMapDiagnostics()
|
||||
const floorIdMap = new Map<string, string>()
|
||||
for (const floor of (diagnostics.floors || []).filter(isIndoorNavigableFloor)) {
|
||||
floorIdMap.set(toAppFloorId(floor.floorId), stringifyId(floor.floorId))
|
||||
}
|
||||
const rawStartFloorId = floorIdMap.get(startTarget.floorId) || startTarget.floorId
|
||||
const rawEndFloorId = floorIdMap.get(endTarget.floorId) || endTarget.floorId
|
||||
|
||||
const route = await this.sdkApiProvider.planRoute({
|
||||
startFloorId: rawStartFloorId,
|
||||
startFloorId: startTarget.floorId,
|
||||
startX: startTarget.positionGltf[0],
|
||||
startY: startTarget.positionGltf[2],
|
||||
startNodeId: numericId(startTarget.routeNodeId) || null,
|
||||
endFloorId: rawEndFloorId,
|
||||
startNodeId: startTarget.routeNodeId || null,
|
||||
endFloorId: endTarget.floorId,
|
||||
endX: endTarget.positionGltf[0],
|
||||
endY: endTarget.positionGltf[2],
|
||||
endNodeId: numericId(endTarget.routeNodeId) || null,
|
||||
endNodeId: endTarget.routeNodeId || null,
|
||||
wheelchair: 0
|
||||
})
|
||||
|
||||
@@ -222,7 +224,7 @@ export class SdkGuideRouteRepository {
|
||||
if (error instanceof GuideRouteError) throw error
|
||||
throw new GuideRouteError(
|
||||
'ROUTE_NOT_FOUND',
|
||||
error instanceof Error ? error.message : 'SGS 路线接口规划失败'
|
||||
error instanceof Error ? error.message : 'SGS 位置关系接口生成失败'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user