@@ -38,6 +38,12 @@ import {
|
||||
|
||||
const SDK_FLOOR_BUNDLE_CONCURRENCY = 2
|
||||
|
||||
const getNow = () => (
|
||||
typeof performance !== 'undefined' && typeof performance.now === 'function'
|
||||
? performance.now()
|
||||
: Date.now()
|
||||
)
|
||||
|
||||
const toRenderPoi = (poi: StaticNavPoiPayload): GuideRenderPoi => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
@@ -77,6 +83,26 @@ const dedupeRenderPoisById = (pois: GuideRenderPoi[]) => {
|
||||
})
|
||||
}
|
||||
|
||||
const countByValue = <T>(
|
||||
items: T[],
|
||||
selector: (item: T) => string | number | null | undefined
|
||||
) => items.reduce<Record<string, number>>((counts, item) => {
|
||||
const key = String(selector(item) || '<missing>')
|
||||
counts[key] = (counts[key] || 0) + 1
|
||||
return counts
|
||||
}, {})
|
||||
|
||||
const countDroppedRenderPoiCategories = (
|
||||
sourcePois: GuideRenderPoi[],
|
||||
keptPois: GuideRenderPoi[]
|
||||
) => {
|
||||
const keptIds = new Set(keptPois.map((poi) => poi.id))
|
||||
return countByValue(
|
||||
sourcePois.filter((poi) => !keptIds.has(poi.id)),
|
||||
(poi) => poi.primaryCategory
|
||||
)
|
||||
}
|
||||
|
||||
const trimTrailingSlash = (value: string) => value.replace(/\/+$/, '')
|
||||
|
||||
const resolveSgsAssetUrl = (url?: string | null) => {
|
||||
@@ -135,6 +161,37 @@ const warnSgsGuideModelDiagnostics = (
|
||||
console.warn(`[SGS guide model] ${message}`, payload)
|
||||
}
|
||||
|
||||
const stringifyDiagnosticPayload = (payload: Record<string, unknown>) => {
|
||||
try {
|
||||
return JSON.stringify(payload)
|
||||
} catch {
|
||||
return '[unserializable]'
|
||||
}
|
||||
}
|
||||
|
||||
const logSgsGuideModelDiagnostics = (
|
||||
message: string,
|
||||
payload: Record<string, unknown>
|
||||
) => {
|
||||
if (!import.meta.env.DEV) return
|
||||
if (typeof window !== 'undefined') {
|
||||
const diagnosticsWindow = window as unknown as {
|
||||
__GUIDE_3D_DIAGNOSTIC_EVENTS__?: Array<{
|
||||
source: string
|
||||
event: string
|
||||
payload: Record<string, unknown>
|
||||
}>
|
||||
}
|
||||
diagnosticsWindow.__GUIDE_3D_DIAGNOSTIC_EVENTS__ ||= []
|
||||
diagnosticsWindow.__GUIDE_3D_DIAGNOSTIC_EVENTS__.push({
|
||||
source: 'SGS guide model',
|
||||
event: message,
|
||||
payload
|
||||
})
|
||||
}
|
||||
console.debug(`[SGS guide model] ${message} ${stringifyDiagnosticPayload(payload)}`)
|
||||
}
|
||||
|
||||
const summarizeYValues = (pois: GuideRenderPoi[]) => {
|
||||
const values = pois
|
||||
.map((poi) => poi.positionGltf?.[1])
|
||||
@@ -228,9 +285,22 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
|
||||
constructor(private readonly provider: SgsSdkApiProvider = defaultSgsSdkApiProvider) {}
|
||||
|
||||
async loadPackage(): Promise<GuideModelRenderPackage> {
|
||||
const startedAt = getNow()
|
||||
const manifest = await this.provider.getManifest()
|
||||
const allFloors = sortSgsFloors(manifest.floors)
|
||||
const floors = allFloors.filter(isIndoorNavigableFloor)
|
||||
const overviewFloor = allFloors.find((floor) => !isIndoorNavigableFloor(floor))
|
||||
const overviewBundleRequest = overviewFloor
|
||||
? this.provider.getFloorBundle(String(overviewFloor.floorId)).catch((error) => {
|
||||
warnSgsGuideModelDiagnostics('overview bundle request failed', {
|
||||
floorId: String(overviewFloor.floorId),
|
||||
floorCode: overviewFloor.floorCode,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
return null
|
||||
})
|
||||
: Promise.resolve(null)
|
||||
|
||||
this.floorsCache = floors
|
||||
|
||||
const bundleResults = await mapWithConcurrency(
|
||||
@@ -291,10 +361,7 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
|
||||
})
|
||||
.filter((asset): asset is GuideModelFloorAsset => Boolean(asset?.modelUrl))
|
||||
|
||||
const overviewFloor = allFloors.find((floor) => !isIndoorNavigableFloor(floor))
|
||||
const overviewBundle = overviewFloor
|
||||
? await this.provider.getFloorBundle(String(overviewFloor.floorId)).catch(() => null)
|
||||
: null
|
||||
const overviewBundle = await overviewBundleRequest
|
||||
const overviewModelUrls = uniqueModelUrls([
|
||||
overviewBundle?.model?.modelUrl,
|
||||
overviewBundle?.model?.fallbackModelUrl,
|
||||
@@ -306,6 +373,15 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
|
||||
...asset,
|
||||
sharedModelAsset: isSharedSgsModelAsset(asset.modelUrl, overviewModelUrl, floorModelUrls)
|
||||
}))
|
||||
logSgsGuideModelDiagnostics('render package ready', {
|
||||
elapsedMs: Math.round(getNow() - startedAt),
|
||||
mapId: manifest.mapId,
|
||||
mode: dataSourceConfig.mode,
|
||||
overviewFloorId: overviewFloor ? String(overviewFloor.floorId) : '',
|
||||
overviewModelUrl,
|
||||
floorCount: floorAssets.length,
|
||||
floorBundleCount: bundleResults.filter((result) => Boolean(result.bundle)).length
|
||||
})
|
||||
|
||||
return {
|
||||
overviewModelUrl,
|
||||
@@ -377,14 +453,28 @@ export class SgsSdkGuideModelRepository implements GuideModelRepository {
|
||||
}
|
||||
|
||||
const hallPois = museumHallPois.map(toSgsRenderPoi)
|
||||
const renderPois = dedupeRenderPoisById([
|
||||
const adaptedPois = dedupeRenderPoisById([
|
||||
...hallPois,
|
||||
...ordinaryPois
|
||||
])
|
||||
.filter((poi) => poi.floorId === resolvedFloorId)
|
||||
const floorMatchedPois = adaptedPois.filter((poi) => poi.floorId === resolvedFloorId)
|
||||
const renderPois = floorMatchedPois
|
||||
.filter((poi) => Array.isArray(poi.positionGltf) && poi.positionGltf.length === 3)
|
||||
|
||||
const renderHallPois = renderPois.filter((poi) => poi.kind === 'hall' || poi.primaryCategory === 'exhibition_hall')
|
||||
logSgsGuideModelDiagnostics('floor POI category diagnostics', {
|
||||
floorId: resolvedFloorId,
|
||||
floorCode: matchedFloor.floorCode,
|
||||
rawPoiCount: pois.length,
|
||||
rawPoiTypeCounts: countByValue(pois, (poi) => poi.type),
|
||||
rawPoiGroupCounts: countByValue(pois, (poi) => poi.poiGroup),
|
||||
adaptedPoiCount: adaptedPois.length,
|
||||
adaptedCategoryCounts: countByValue(adaptedPois, (poi) => poi.primaryCategory),
|
||||
adaptedPoiCategoryCount: adaptedPois.filter((poi) => poi.primaryCategory === 'poi').length,
|
||||
renderPoiCount: renderPois.length,
|
||||
renderPoiCategoryCount: renderPois.filter((poi) => poi.primaryCategory === 'poi').length,
|
||||
repositoryDroppedCategoryCounts: countDroppedRenderPoiCategories(adaptedPois, renderPois)
|
||||
})
|
||||
warnSgsGuideModelDiagnostics('floor render POI diagnostics', {
|
||||
floorId: resolvedFloorId,
|
||||
floorCode: matchedFloor.floorCode,
|
||||
|
||||
Reference in New Issue
Block a user