feat: wire guide data source and POI focus UI
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,6 @@
|
||||
|
||||
这些 JSON 是早期演示数据,当前导览模块不再以这里作为数据源。
|
||||
|
||||
当前室内导览、设施搜索、位置预览统一从:
|
||||
当前室内导览、设施搜索、位置预览统一通过 Provider / Adapter / Repository / UseCase 数据边界读取 `static/nav-assets/app_nav_assets_v2_clean_20260611_093623`,页面和展示组件不要直接依赖静态包 JSON 文件。
|
||||
|
||||
`/static/nav-assets/app_nav_assets_v2_clean_20260609_075339/app_nav_manifest.json`
|
||||
|
||||
以及同目录下的 `data/poi_all.json`、`data/poi_by_floor/*.json` 读取。
|
||||
|
||||
如需继续改造历史页面,请优先接入 `src/services/navAssets.ts`,不要新增对本目录 JSON 的业务依赖。
|
||||
如需继续改造历史页面,请优先接入 `GuideRepository`、`MuseumContentRepository`、`ExplainRepository` 或对应 use case,不要新增对本目录 JSON 的业务依赖。
|
||||
|
||||
@@ -206,15 +206,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import type {
|
||||
ExplainExhibitViewModel
|
||||
} from '@/view-models/explainViewModels'
|
||||
|
||||
export interface Exhibit {
|
||||
id: string
|
||||
name: string
|
||||
artist?: string
|
||||
hall?: string
|
||||
floor?: string
|
||||
image?: string
|
||||
hasAudio?: boolean
|
||||
export interface Exhibit extends ExplainExhibitViewModel {
|
||||
audioUrl?: string
|
||||
}
|
||||
|
||||
@@ -225,6 +221,16 @@ interface SearchResult {
|
||||
type: 'exhibit' | 'hall' | 'facility'
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
exhibits?: Exhibit[]
|
||||
hotSearches?: string[]
|
||||
initialSearchHistory?: string[]
|
||||
}>(), {
|
||||
exhibits: () => [],
|
||||
hotSearches: () => ['恐龙化石', '银杏', '海洋生物', '霸王龙', '古植物', '矿石标本', '恐龙', '化石'],
|
||||
initialSearchHistory: () => ['霸王龙', '化石', '恐龙']
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
exhibitClick: [exhibit: Exhibit]
|
||||
audioClick: [exhibit: Exhibit]
|
||||
@@ -259,46 +265,31 @@ let touchStartY = 0
|
||||
let touchCurrentY = 0
|
||||
|
||||
// 热门搜索
|
||||
const hotSearches = ['恐龙化石', '银杏', '海洋生物', '霸王龙', '古植物', '矿石标本', '恐龙', '化石']
|
||||
const hotSearches = computed(() => props.hotSearches)
|
||||
|
||||
// 搜索历史
|
||||
const searchHistory = ref<string[]>(['霸王龙', '化石', '恐龙'])
|
||||
const searchHistory = ref<string[]>([...props.initialSearchHistory])
|
||||
|
||||
// 搜索数据
|
||||
const allData: SearchResult[] = [
|
||||
{ id: '1', name: '霸王龙化石骨架', desc: '恐龙厅 · 2F', type: 'exhibit' },
|
||||
{ id: '2', name: '银杏化石', desc: '演化厅 · B1', type: 'exhibit' },
|
||||
{ id: '3', name: '三叶虫化石', desc: '演化厅 · B1', type: 'exhibit' },
|
||||
{ id: '4', name: '腕龙骨骼', desc: '恐龙厅 · 2F', type: 'exhibit' },
|
||||
{ id: '5', name: '猛犸象象牙', desc: '人类厅 · 3F', type: 'exhibit' },
|
||||
{ id: 'h1', name: '恐龙厅', desc: '2F · 25件展品', type: 'hall' },
|
||||
{ id: 'h2', name: '演化厅', desc: 'B1 · 30件展品', type: 'hall' },
|
||||
{ id: 'h3', name: '生物厅', desc: '3F · 18件展品', type: 'hall' },
|
||||
{ id: 'f1', name: '洗手间', desc: '1F', type: 'facility' },
|
||||
{ id: 'f2', name: '咖啡厅', desc: '1F', type: 'facility' },
|
||||
]
|
||||
const allData = computed<SearchResult[]>(() => props.exhibits.map((exhibit) => ({
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
desc: exhibit.hall || exhibit.floor || '展项内容',
|
||||
type: 'exhibit'
|
||||
})))
|
||||
|
||||
// 搜索结果计算
|
||||
const searchResults = computed(() => {
|
||||
if (!searchInputValue.value.trim()) return []
|
||||
const kw = searchInputValue.value.toLowerCase()
|
||||
return allData.filter(item =>
|
||||
return allData.value.filter(item =>
|
||||
item.name.toLowerCase().includes(kw) ||
|
||||
item.desc.toLowerCase().includes(kw)
|
||||
)
|
||||
})
|
||||
|
||||
// 展品列表
|
||||
const exhibits = ref<Exhibit[]>([
|
||||
{ id: '1', name: '银杏化石', hall: '演化厅 B1', hasAudio: true, audioUrl: '' },
|
||||
{ id: '2', name: '三叶虫化石', hall: '演化厅 B1', hasAudio: true, audioUrl: '' },
|
||||
{ id: '3', name: '腕龙骨骼', hall: '恐龙厅 2F', hasAudio: true, audioUrl: '' },
|
||||
{ id: '4', name: '始祖鸟标本', hall: '演化厅 B1', hasAudio: false, audioUrl: '' },
|
||||
{ id: '5', name: '猛犸象象牙', hall: '人类厅 3F', hasAudio: true, audioUrl: '' },
|
||||
{ id: '6', name: '珊瑚礁标本', hall: '生物厅 3F', hasAudio: false, audioUrl: '' },
|
||||
{ id: '7', name: '剑龙化石', hall: '恐龙厅 2F', hasAudio: true, audioUrl: '' },
|
||||
{ id: '8', name: '鹦鹉螺化石', hall: '演化厅 B1', hasAudio: true, audioUrl: '' }
|
||||
])
|
||||
const exhibits = computed(() => props.exhibits)
|
||||
|
||||
// 过滤展品
|
||||
const filteredExhibits = computed(() => {
|
||||
|
||||
@@ -30,16 +30,9 @@ const emit = defineEmits<{
|
||||
change: [floorId: string]
|
||||
}>()
|
||||
|
||||
const currentFloor = ref(props.current || '1F')
|
||||
const currentFloor = ref(props.current || props.floors?.[0]?.id || '')
|
||||
|
||||
const defaultFloors: Floor[] = [
|
||||
{ id: '3F', label: '3F' },
|
||||
{ id: '2F', label: '2F' },
|
||||
{ id: '1F', label: '1F' },
|
||||
{ id: 'B1', label: 'B1' }
|
||||
]
|
||||
|
||||
const floors = props.floors || defaultFloors
|
||||
const floors = props.floors || []
|
||||
|
||||
const handleFloorClick = (floor: Floor) => {
|
||||
if (floor.disabled) return
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { computed, ref, onMounted } from 'vue'
|
||||
|
||||
const isH5 = typeof window !== 'undefined'
|
||||
|
||||
@@ -94,6 +94,19 @@ interface Polygon {
|
||||
zIndex: number
|
||||
}
|
||||
|
||||
interface MapFloor {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
activeFloor?: string
|
||||
floors?: MapFloor[]
|
||||
}>(), {
|
||||
activeFloor: '1F',
|
||||
floors: () => [] as MapFloor[]
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
markerClick: [markerId: number]
|
||||
floorChange: [floor: string]
|
||||
@@ -111,15 +124,10 @@ const mapCenter = ref<MapCenter>({
|
||||
const mapScale = ref(17)
|
||||
|
||||
// 当前楼层
|
||||
const currentFloor = ref('1F')
|
||||
const currentFloor = ref(props.activeFloor)
|
||||
|
||||
// 楼层列表
|
||||
const floors = [
|
||||
{ id: '3F', label: '3F' },
|
||||
{ id: '2F', label: '2F' },
|
||||
{ id: '1F', label: '1F' },
|
||||
{ id: 'B1', label: 'B1' }
|
||||
]
|
||||
const floors = computed(() => props.floors)
|
||||
|
||||
// 博物馆建筑围栏(多边形)
|
||||
// 深圳自然博物馆实际建筑轮廓坐标
|
||||
|
||||
@@ -50,56 +50,23 @@ import * as THREE from 'three'
|
||||
import { GLTFLoader, type GLTF } from 'three/addons/loaders/GLTFLoader.js'
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
|
||||
import FloorSwitcher from './FloorSwitcher.vue'
|
||||
import { NAV_ASSET_BASE_URL } from '@/services/navAssets'
|
||||
import type {
|
||||
GuideModelFloorAsset,
|
||||
GuideModelRenderPackage,
|
||||
GuideModelSource,
|
||||
GuideRenderPoi
|
||||
} from '@/domain/guideModel'
|
||||
|
||||
type ViewMode = 'overview' | 'floor'
|
||||
|
||||
interface ManifestFloorModel {
|
||||
floorId: string
|
||||
order: number
|
||||
asset: string
|
||||
}
|
||||
|
||||
interface CleanNavManifest {
|
||||
status: string
|
||||
assets: {
|
||||
overviewModel: {
|
||||
asset: string
|
||||
}
|
||||
floorModels: ManifestFloorModel[]
|
||||
}
|
||||
data: {
|
||||
floorIndex: string
|
||||
}
|
||||
}
|
||||
|
||||
interface FloorIndexItem {
|
||||
floorId: string
|
||||
order: number
|
||||
modelAsset: string
|
||||
poiDataAsset: string
|
||||
poiCount: number
|
||||
}
|
||||
|
||||
interface FloorIndexData {
|
||||
status: string
|
||||
floors: FloorIndexItem[]
|
||||
}
|
||||
type FloorIndexItem = GuideModelFloorAsset
|
||||
|
||||
interface FloorOption {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
interface CleanPoi {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
primaryCategory: string
|
||||
primaryCategoryZh: string
|
||||
iconType: string
|
||||
positionGltf?: [number, number, number]
|
||||
}
|
||||
type RenderPoi = GuideRenderPoi
|
||||
|
||||
interface TargetPoiFocusRequest {
|
||||
requestId: number | string
|
||||
@@ -119,23 +86,18 @@ interface TargetPoiFocusResult {
|
||||
message?: string
|
||||
}
|
||||
|
||||
interface FloorPoiData {
|
||||
status: string
|
||||
floorId: string
|
||||
pois: CleanPoi[]
|
||||
}
|
||||
|
||||
type ContainerRef = HTMLElement | { $el?: HTMLElement } | null
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
assetBaseUrl?: string
|
||||
modelSource: GuideModelSource
|
||||
initialFloorId?: string
|
||||
initialView?: ViewMode
|
||||
showControls?: boolean
|
||||
showPoi?: boolean
|
||||
targetFocus?: TargetPoiFocusRequest | null
|
||||
}>(), {
|
||||
assetBaseUrl: NAV_ASSET_BASE_URL,
|
||||
assetBaseUrl: '',
|
||||
initialFloorId: 'L1',
|
||||
initialView: 'overview',
|
||||
showControls: true,
|
||||
@@ -145,7 +107,7 @@ const props = withDefaults(defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
floorChange: [floorId: string]
|
||||
poiClick: [poi: CleanPoi]
|
||||
poiClick: [poi: RenderPoi]
|
||||
targetFocus: [result: TargetPoiFocusResult]
|
||||
}>()
|
||||
|
||||
@@ -156,9 +118,10 @@ const loadingProgress = ref(0)
|
||||
const loadingMessage = ref('正在读取室内导览资源...')
|
||||
const activeView = ref<ViewMode>(props.initialView)
|
||||
const currentFloor = ref(props.initialFloorId)
|
||||
const selectedPOI = ref<CleanPoi | null>(null)
|
||||
const selectedPOI = ref<RenderPoi | null>(null)
|
||||
const activeFocusPoiId = ref('')
|
||||
const floorIndex = ref<FloorIndexItem[]>([])
|
||||
const renderPackage = ref<GuideModelRenderPackage | null>(null)
|
||||
|
||||
const floors = computed<FloorOption[]>(() => (
|
||||
[...floorIndex.value]
|
||||
@@ -170,7 +133,6 @@ const floors = computed<FloorOption[]>(() => (
|
||||
))
|
||||
const shouldRenderPoiMarkers = computed(() => props.showPoi || props.showControls || Boolean(props.targetFocus))
|
||||
|
||||
let manifest: CleanNavManifest | null = null
|
||||
let scene: THREE.Scene | null = null
|
||||
let camera: THREE.PerspectiveCamera | null = null
|
||||
let renderer: THREE.WebGLRenderer | null = null
|
||||
@@ -178,16 +140,13 @@ let controls: OrbitControls | null = null
|
||||
let loader: GLTFLoader | null = null
|
||||
let activeModel: THREE.Object3D | null = null
|
||||
let poiGroup: THREE.Group | null = null
|
||||
let activeFocusLabelSprite: THREE.Sprite | null = null
|
||||
let animationId = 0
|
||||
let resizeObserver: ResizeObserver | null = null
|
||||
let isDisposed = false
|
||||
let pendingTargetFocus: TargetPoiFocusRequest | null = null
|
||||
let targetFocusQueue: Promise<unknown> = Promise.resolve()
|
||||
|
||||
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
|
||||
|
||||
const assetUrl = (relativePath: string) => `${normalizeBaseUrl(props.assetBaseUrl)}/${relativePath.replace(/^\/+/, '')}`
|
||||
|
||||
const getContainerElement = () => {
|
||||
const container = containerRef.value
|
||||
if (!container) return null
|
||||
@@ -202,27 +161,15 @@ const getContainerElement = () => {
|
||||
return element instanceof HTMLElement ? element : null
|
||||
}
|
||||
|
||||
const formatFloorLabel = (floorId: string) => {
|
||||
if (floorId === 'L-2') return 'B2'
|
||||
if (floorId === 'L-1') return 'B1'
|
||||
if (floorId === 'L1.5') return '1.5F'
|
||||
if (floorId.startsWith('L')) return `${floorId.slice(1)}F`
|
||||
return floorId
|
||||
}
|
||||
const formatFloorLabel = (floorId: string) => (
|
||||
floorIndex.value.find((floor) => floor.floorId === floorId)?.label || floorId
|
||||
)
|
||||
|
||||
const setProgress = (progress: number, message: string) => {
|
||||
loadingProgress.value = Math.min(100, Math.max(0, progress))
|
||||
loadingMessage.value = message
|
||||
}
|
||||
|
||||
const fetchJson = async <T,>(url: string): Promise<T> => {
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) {
|
||||
throw new Error(`资源读取失败: ${response.status} ${url}`)
|
||||
}
|
||||
return response.json() as Promise<T>
|
||||
}
|
||||
|
||||
const waitForContainer = async () => {
|
||||
await nextTick()
|
||||
|
||||
@@ -274,7 +221,7 @@ const initThree = async () => {
|
||||
|
||||
loader = new GLTFLoader()
|
||||
poiGroup = new THREE.Group()
|
||||
poiGroup.name = 'CleanPackagePOI'
|
||||
poiGroup.name = 'GuideModelPOI'
|
||||
scene.add(poiGroup)
|
||||
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 1.6)
|
||||
@@ -340,6 +287,7 @@ const clearSceneData = () => {
|
||||
}
|
||||
|
||||
activeModel = null
|
||||
disposeFocusLabel()
|
||||
selectedPOI.value = null
|
||||
|
||||
if (poiGroup) {
|
||||
@@ -405,15 +353,16 @@ const fitCameraToObject = (object: THREE.Object3D) => {
|
||||
}
|
||||
|
||||
const loadOverview = async () => {
|
||||
if (!manifest || !scene) return
|
||||
const packageData = renderPackage.value
|
||||
if (!packageData || !scene) return
|
||||
|
||||
activeView.value = 'overview'
|
||||
clearSceneData()
|
||||
setProgress(18, '正在加载全馆三维模型...')
|
||||
|
||||
const gltf = await loadModel(assetUrl(manifest.assets.overviewModel.asset), '正在加载全馆三维模型')
|
||||
const gltf = await loadModel(packageData.overviewModelUrl, '正在加载全馆三维模型')
|
||||
activeModel = gltf.scene
|
||||
activeModel.name = 'CleanOverviewModel'
|
||||
activeModel.name = 'GuideOverviewModel'
|
||||
prepareModel(activeModel)
|
||||
scene.add(activeModel)
|
||||
fitCameraToObject(activeModel)
|
||||
@@ -428,9 +377,9 @@ const loadFloor = async (floorId: string) => {
|
||||
clearSceneData()
|
||||
setProgress(18, `正在加载 ${formatFloorLabel(floor.floorId)} 模型...`)
|
||||
|
||||
const gltf = await loadModel(assetUrl(floor.modelAsset), `正在加载 ${formatFloorLabel(floor.floorId)} 模型`)
|
||||
const gltf = await loadModel(floor.modelUrl, `正在加载 ${formatFloorLabel(floor.floorId)} 模型`)
|
||||
activeModel = gltf.scene
|
||||
activeModel.name = `CleanFloorModel_${floor.floorId}`
|
||||
activeModel.name = `GuideFloorModel_${floor.floorId}`
|
||||
prepareModel(activeModel)
|
||||
scene.add(activeModel)
|
||||
fitCameraToObject(activeModel)
|
||||
@@ -440,7 +389,7 @@ const loadFloor = async (floorId: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
const createPoiMaterial = (poi: CleanPoi) => {
|
||||
const createPoiMaterial = (poi: RenderPoi) => {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 96
|
||||
canvas.height = 96
|
||||
@@ -476,6 +425,69 @@ const createPoiMaterial = (poi: CleanPoi) => {
|
||||
})
|
||||
}
|
||||
|
||||
const createPoiLabelSprite = (poi: RenderPoi, markerSize: number) => {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = 512
|
||||
canvas.height = 144
|
||||
const context = canvas.getContext('2d')
|
||||
const label = poi.name.length > 14 ? `${poi.name.slice(0, 14)}…` : poi.name
|
||||
|
||||
if (context) {
|
||||
context.clearRect(0, 0, canvas.width, canvas.height)
|
||||
context.fillStyle = 'rgba(0, 0, 0, 0.82)'
|
||||
context.beginPath()
|
||||
context.roundRect(36, 18, 440, 82, 30)
|
||||
context.fill()
|
||||
|
||||
context.beginPath()
|
||||
context.moveTo(238, 100)
|
||||
context.lineTo(274, 100)
|
||||
context.lineTo(256, 124)
|
||||
context.closePath()
|
||||
context.fill()
|
||||
|
||||
context.font = '600 38px sans-serif'
|
||||
context.textAlign = 'center'
|
||||
context.textBaseline = 'middle'
|
||||
context.fillStyle = '#ffffff'
|
||||
context.fillText(label, 256, 59, 390)
|
||||
}
|
||||
|
||||
const texture = new THREE.CanvasTexture(canvas)
|
||||
texture.colorSpace = THREE.SRGBColorSpace
|
||||
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({
|
||||
map: texture,
|
||||
transparent: true,
|
||||
depthTest: false,
|
||||
depthWrite: false
|
||||
}))
|
||||
sprite.userData.isPoiLabel = true
|
||||
sprite.renderOrder = 30
|
||||
sprite.scale.set(markerSize * 4.8, markerSize * 1.35, 1)
|
||||
|
||||
return sprite
|
||||
}
|
||||
|
||||
const disposeFocusLabel = () => {
|
||||
if (!activeFocusLabelSprite) return
|
||||
|
||||
activeFocusLabelSprite.parent?.remove(activeFocusLabelSprite)
|
||||
disposeObject(activeFocusLabelSprite)
|
||||
activeFocusLabelSprite = null
|
||||
}
|
||||
|
||||
const showFocusPoiLabel = (poi: RenderPoi) => {
|
||||
if (!poiGroup || !poi.positionGltf) return
|
||||
|
||||
disposeFocusLabel()
|
||||
|
||||
const markerSize = getPoiMarkerSize()
|
||||
const [x, y, z] = poi.positionGltf
|
||||
activeFocusLabelSprite = createPoiLabelSprite(poi, markerSize)
|
||||
activeFocusLabelSprite.position.set(x, y + markerSize * 2.35, z)
|
||||
poiGroup.add(activeFocusLabelSprite)
|
||||
}
|
||||
|
||||
const getPoiColor = (category: string) => {
|
||||
const colorMap: Record<string, string> = {
|
||||
touring_poi: '#2f6fed',
|
||||
@@ -509,8 +521,8 @@ const updatePoiMarkerFocus = () => {
|
||||
if (!poiGroup) return
|
||||
|
||||
poiGroup.children.forEach((child) => {
|
||||
if (child instanceof THREE.Sprite) {
|
||||
const poi = child.userData.poi as CleanPoi | undefined
|
||||
if (child instanceof THREE.Sprite && !child.userData.isPoiLabel) {
|
||||
const poi = child.userData.poi as RenderPoi | undefined
|
||||
setPoiSpriteFocusStyle(child, Boolean(poi && poi.id === activeFocusPoiId.value))
|
||||
}
|
||||
})
|
||||
@@ -518,15 +530,15 @@ const updatePoiMarkerFocus = () => {
|
||||
|
||||
const findPoiSprite = (poiId: string) => (
|
||||
poiGroup?.children.find((child): child is THREE.Sprite => (
|
||||
child instanceof THREE.Sprite && (child.userData.poi as CleanPoi | undefined)?.id === poiId
|
||||
child instanceof THREE.Sprite && (child.userData.poi as RenderPoi | undefined)?.id === poiId
|
||||
))
|
||||
)
|
||||
|
||||
const findLoadedPoi = (poiId: string) => (
|
||||
findPoiSprite(poiId)?.userData.poi as CleanPoi | undefined
|
||||
findPoiSprite(poiId)?.userData.poi as RenderPoi | undefined
|
||||
)
|
||||
|
||||
const createPoiFromFocusRequest = (request: TargetPoiFocusRequest): CleanPoi | null => {
|
||||
const createPoiFromFocusRequest = (request: TargetPoiFocusRequest): RenderPoi | null => {
|
||||
if (!request.positionGltf) return null
|
||||
|
||||
return {
|
||||
@@ -561,8 +573,7 @@ const addFocusMarkerFromRequest = (request: TargetPoiFocusRequest) => {
|
||||
const loadFloorPOIs = async (floor: FloorIndexItem) => {
|
||||
if (!poiGroup) return
|
||||
|
||||
const data = await fetchJson<FloorPoiData>(assetUrl(floor.poiDataAsset))
|
||||
const validPois = data.pois.filter((poi) => Array.isArray(poi.positionGltf) && poi.positionGltf.length === 3)
|
||||
const validPois = await props.modelSource.loadFloorPois(floor.floorId)
|
||||
const markerSize = getPoiMarkerSize()
|
||||
|
||||
validPois.forEach((poi) => {
|
||||
@@ -588,8 +599,10 @@ const handlePointerDown = (event: PointerEvent) => {
|
||||
raycaster.setFromCamera(pointer, camera)
|
||||
const hits = raycaster.intersectObjects(poiGroup.children, false)
|
||||
|
||||
if (hits[0]?.object.userData.poi) {
|
||||
selectedPOI.value = hits[0].object.userData.poi as CleanPoi
|
||||
const hit = hits.find((item) => !item.object.userData.isPoiLabel && item.object.userData.poi)
|
||||
|
||||
if (hit?.object.userData.poi) {
|
||||
selectedPOI.value = hit.object.userData.poi as RenderPoi
|
||||
activeFocusPoiId.value = selectedPOI.value.id
|
||||
updatePoiMarkerFocus()
|
||||
emit('poiClick', selectedPOI.value)
|
||||
@@ -614,6 +627,7 @@ const clearTargetFocus = () => {
|
||||
pendingTargetFocus = null
|
||||
activeFocusPoiId.value = ''
|
||||
selectedPOI.value = null
|
||||
disposeFocusLabel()
|
||||
updatePoiMarkerFocus()
|
||||
}
|
||||
|
||||
@@ -623,7 +637,7 @@ const isSceneReadyForTargetFocus = () => (
|
||||
&& !loadError.value
|
||||
)
|
||||
|
||||
const focusCameraOnPoi = (poi: CleanPoi) => {
|
||||
const focusCameraOnPoi = (poi: RenderPoi) => {
|
||||
if (!camera || !controls || !poi.positionGltf) return
|
||||
|
||||
const [x, y, z] = poi.positionGltf
|
||||
@@ -675,6 +689,7 @@ const focusTargetPoi = async (request: TargetPoiFocusRequest) => {
|
||||
|
||||
if (!poi?.positionGltf) {
|
||||
selectedPOI.value = null
|
||||
disposeFocusLabel()
|
||||
updatePoiMarkerFocus()
|
||||
emitTargetFocus(request, 'missing', '目标暂无三维坐标')
|
||||
return false
|
||||
@@ -682,6 +697,7 @@ const focusTargetPoi = async (request: TargetPoiFocusRequest) => {
|
||||
|
||||
selectedPOI.value = poi
|
||||
updatePoiMarkerFocus()
|
||||
showFocusPoiLabel(poi)
|
||||
focusCameraOnPoi(poi)
|
||||
emitTargetFocus(request, 'focused')
|
||||
return true
|
||||
@@ -718,17 +734,13 @@ const queueTargetFocus = (request: TargetPoiFocusRequest | null) => {
|
||||
})
|
||||
}
|
||||
|
||||
const loadCleanPackage = async () => {
|
||||
const loadModelPackage = async () => {
|
||||
setProgress(8, '正在读取室内导览资源...')
|
||||
manifest = await fetchJson<CleanNavManifest>(assetUrl('app_nav_manifest.json'))
|
||||
|
||||
if (manifest.status !== 'pass') {
|
||||
throw new Error('室内导览资源包状态不是 pass')
|
||||
}
|
||||
const packageData = await props.modelSource.loadPackage()
|
||||
renderPackage.value = packageData
|
||||
|
||||
setProgress(14, '正在读取楼层索引...')
|
||||
const floorData = await fetchJson<FloorIndexData>(assetUrl(manifest.data.floorIndex))
|
||||
floorIndex.value = [...floorData.floors].sort((a, b) => a.order - b.order)
|
||||
floorIndex.value = [...packageData.floors].sort((a, b) => a.order - b.order)
|
||||
|
||||
if (!floorIndex.value.some((floor) => floor.floorId === currentFloor.value)) {
|
||||
currentFloor.value = floorIndex.value.find((floor) => floor.floorId === 'L1')?.floorId || floorIndex.value[0]?.floorId || 'L1'
|
||||
@@ -752,13 +764,13 @@ const init3DScene = async () => {
|
||||
setProgress(0, '正在初始化三维场景...')
|
||||
|
||||
await initThree()
|
||||
await loadCleanPackage()
|
||||
await loadModelPackage()
|
||||
|
||||
setProgress(100, '室内三维模型加载完成')
|
||||
isLoading.value = false
|
||||
queueTargetFocus(pendingTargetFocus || props.targetFocus || null)
|
||||
} catch (error) {
|
||||
console.error('室内 3D clean package 加载失败:', error)
|
||||
console.error('室内 3D 模型资源加载失败:', error)
|
||||
loadError.value = true
|
||||
isLoading.value = false
|
||||
setProgress(0, error instanceof Error ? error.message : '请检查模型资源或网络状态后重试')
|
||||
@@ -840,11 +852,12 @@ defineExpose({
|
||||
clearNavigation: () => {
|
||||
activeFocusPoiId.value = ''
|
||||
selectedPOI.value = null
|
||||
disposeFocusLabel()
|
||||
updatePoiMarkerFocus()
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => props.assetBaseUrl, () => {
|
||||
watch(() => props.modelSource, () => {
|
||||
init3DScene()
|
||||
})
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
<template v-if="mapType === 'indoor'">
|
||||
<!-- #ifdef H5 -->
|
||||
<ThreeMap
|
||||
v-if="indoorModelSource"
|
||||
ref="threeMapRef"
|
||||
class="indoor-three-map"
|
||||
:asset-base-url="indoorAssetBaseUrl"
|
||||
:model-source="indoorModelSource"
|
||||
:initial-floor-id="activeFloorId"
|
||||
:initial-view="indoorInitialView"
|
||||
:show-controls="false"
|
||||
@@ -23,7 +25,11 @@
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
<view v-else class="outdoor-map-bg">
|
||||
<TencentMap class="outdoor-map" />
|
||||
<TencentMap
|
||||
class="outdoor-map"
|
||||
:active-floor="activeFloor"
|
||||
:floors="props.floors"
|
||||
/>
|
||||
</view>
|
||||
<view v-if="dimmed" class="dim-layer"></view>
|
||||
</view>
|
||||
@@ -105,7 +111,7 @@
|
||||
|
||||
<view v-if="showFloor" class="floor-switcher" :style="floorSwitcherStyle">
|
||||
<view
|
||||
v-for="floor in floors"
|
||||
v-for="floor in floorLabels"
|
||||
:key="floor"
|
||||
class="floor-item"
|
||||
:class="{ active: activeFloor === floor }"
|
||||
@@ -136,11 +142,14 @@ import TencentMap from '@/components/map/TencentMap.vue'
|
||||
// #ifdef H5
|
||||
import ThreeMap from '@/components/map/ThreeMap.vue'
|
||||
// #endif
|
||||
import {
|
||||
NAV_ASSET_BASE_URL,
|
||||
NAV_FLOOR_OPTIONS,
|
||||
navFloorIdFromLabel
|
||||
} from '@/services/navAssets'
|
||||
import type {
|
||||
GuideModelSource
|
||||
} from '@/domain/guideModel'
|
||||
|
||||
interface GuideFloorOption {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
interface TargetPoiFocusRequest {
|
||||
requestId: number | string
|
||||
@@ -183,6 +192,9 @@ const props = withDefaults(defineProps<{
|
||||
mapType?: 'indoor' | 'outdoor'
|
||||
outdoorVariant?: 'home' | 'entrance'
|
||||
indoorAssetBaseUrl?: string
|
||||
indoorModelSource?: GuideModelSource
|
||||
floors?: GuideFloorOption[]
|
||||
normalizeFloorId?: (labelOrId: string) => string
|
||||
dimmed?: boolean
|
||||
targetFocusRequest?: TargetPoiFocusRequest | null
|
||||
}>(), {
|
||||
@@ -205,7 +217,10 @@ const props = withDefaults(defineProps<{
|
||||
modeStatusTone: 'solid',
|
||||
mapType: 'indoor',
|
||||
outdoorVariant: 'home',
|
||||
indoorAssetBaseUrl: NAV_ASSET_BASE_URL,
|
||||
indoorAssetBaseUrl: '',
|
||||
indoorModelSource: undefined,
|
||||
floors: () => [] as GuideFloorOption[],
|
||||
normalizeFloorId: (labelOrId: string) => labelOrId,
|
||||
dimmed: false,
|
||||
targetFocusRequest: null
|
||||
})
|
||||
@@ -223,9 +238,11 @@ const threeMapRef = ref<{
|
||||
switchFloor?: (floorId: string) => Promise<void> | void
|
||||
showOverview?: () => Promise<void> | void
|
||||
} | null>(null)
|
||||
const floors = NAV_FLOOR_OPTIONS.map((floor) => floor.label)
|
||||
|
||||
const activeFloorId = computed(() => navFloorIdFromLabel(props.activeFloor))
|
||||
const floorLabels = computed(() => props.floors.map((floor) => floor.label))
|
||||
const indoorModelSource = computed(() => props.indoorModelSource)
|
||||
|
||||
const activeFloorId = computed(() => props.normalizeFloorId(props.activeFloor))
|
||||
|
||||
const searchFieldStyle = computed(() => ({
|
||||
top: props.searchTop
|
||||
@@ -258,7 +275,7 @@ const handleModeChange = (mode: '2d' | '3d') => {
|
||||
}
|
||||
|
||||
const handleFloorChange = (floor: string) => {
|
||||
const floorId = navFloorIdFromLabel(floor)
|
||||
const floorId = props.normalizeFloorId(floor)
|
||||
void threeMapRef.value?.switchFloor?.(floorId)
|
||||
emit('indoorViewChange', 'floor')
|
||||
emit('floorChange', floor)
|
||||
@@ -507,10 +524,11 @@ const handleTargetFocus = (result: TargetPoiFocusResult) => {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
width: 46px;
|
||||
height: 218px;
|
||||
height: auto;
|
||||
max-height: calc(100vh - 260px);
|
||||
padding: 1px;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
background: rgba(255, 255, 255, 0.84);
|
||||
border: 1px solid #dde5df;
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<text class="action-text">查看室外地图</text>
|
||||
</view>
|
||||
<view class="action-btn primary" @tap="handleShowTargetLocation">
|
||||
<text class="action-text">查看目标位置</text>
|
||||
<text class="action-text">重新定位目标</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -107,14 +107,9 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch, nextTick, onMounted } from 'vue'
|
||||
import {
|
||||
formatNavFloorLabel,
|
||||
searchCleanNavPois,
|
||||
type CleanNavPoi
|
||||
} from '@/services/navAssets'
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
|
||||
interface SearchResult {
|
||||
export interface SearchResult {
|
||||
id: string
|
||||
name: string
|
||||
desc: string
|
||||
@@ -126,9 +121,11 @@ const props = withDefaults(defineProps<{
|
||||
placeholder?: string
|
||||
/** 嵌入模式:隐藏背景遮罩和取消按钮,直接嵌入父容器 */
|
||||
embedded?: boolean
|
||||
results?: SearchResult[]
|
||||
}>(), {
|
||||
placeholder: '搜索展品、展厅、设施...',
|
||||
embedded: false
|
||||
embedded: false,
|
||||
results: () => []
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -144,14 +141,7 @@ const hotSearches = ref(['卫生间', '电梯', '服务台', '无障碍', '入
|
||||
|
||||
const searchHistory = ref<string[]>([])
|
||||
|
||||
const allData = ref<SearchResult[]>([])
|
||||
|
||||
const toSearchResult = (poi: CleanNavPoi): SearchResult => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
desc: `${formatNavFloorLabel(poi.floorId)} · ${poi.primaryCategoryZh}`,
|
||||
type: poi.primaryCategory === 'touring_poi' ? 'hall' : 'facility'
|
||||
})
|
||||
const allData = computed(() => props.results)
|
||||
|
||||
// 搜索结果计算
|
||||
const allResults = computed(() => {
|
||||
@@ -218,20 +208,6 @@ const getResultIcon = (type: string): string => {
|
||||
return icons[type] || '📍'
|
||||
}
|
||||
|
||||
const loadCleanResults = async () => {
|
||||
try {
|
||||
const pois = await searchCleanNavPois()
|
||||
allData.value = pois.slice(0, 120).map(toSearchResult)
|
||||
} catch (error) {
|
||||
console.error('加载 clean POI 搜索数据失败:', error)
|
||||
allData.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCleanResults()
|
||||
})
|
||||
|
||||
// 打开时聚焦输入框
|
||||
watch(() => props.isOpen, async (newVal) => {
|
||||
if (newVal) {
|
||||
|
||||
60
src/data/adapters/navAssetsAdapter.ts
Normal file
60
src/data/adapters/navAssetsAdapter.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import type {
|
||||
MuseumCategory,
|
||||
MuseumPoi
|
||||
} from '@/domain/museum'
|
||||
import type {
|
||||
StaticNavPoiCategoryPayload,
|
||||
StaticNavPoiPayload
|
||||
} from '@/data/providers/staticNavAssetsProvider'
|
||||
|
||||
const floorIdPattern = /^L(-?\d+(?:\.\d+)?)$/
|
||||
|
||||
export const formatNavFloorLabel = (floorId: string) => {
|
||||
const match = floorId.match(floorIdPattern)
|
||||
if (!match) return floorId
|
||||
|
||||
const value = Number(match[1])
|
||||
if (Number.isNaN(value)) return floorId
|
||||
|
||||
return value < 0 ? `B${Math.abs(value)}` : `${value}F`
|
||||
}
|
||||
|
||||
export const navFloorIdFromLabel = (labelOrId: string) => {
|
||||
if (floorIdPattern.test(labelOrId)) return labelOrId
|
||||
|
||||
const basementMatch = labelOrId.match(/^B(\d+(?:\.\d+)?)$/i)
|
||||
if (basementMatch) return `L-${basementMatch[1]}`
|
||||
|
||||
const floorMatch = labelOrId.match(/^(\d+(?:\.\d+)?)F$/i)
|
||||
if (floorMatch) return `L${floorMatch[1]}`
|
||||
|
||||
return labelOrId
|
||||
}
|
||||
|
||||
const toCategory = (category: StaticNavPoiCategoryPayload): MuseumCategory => ({
|
||||
id: category.topCategory,
|
||||
label: category.topCategoryZh,
|
||||
iconType: category.iconType
|
||||
})
|
||||
|
||||
const isPoiAccessible = (poi: StaticNavPoiPayload) => (
|
||||
poi.primaryCategory === 'accessibility_special_service'
|
||||
|| poi.categories?.some((category) => category.topCategory === 'accessibility_special_service') === true
|
||||
)
|
||||
|
||||
export const toMuseumPoi = (poi: StaticNavPoiPayload): MuseumPoi => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
floorId: poi.floorId,
|
||||
floorLabel: formatNavFloorLabel(poi.floorId),
|
||||
primaryCategory: {
|
||||
id: poi.primaryCategory,
|
||||
label: poi.primaryCategoryZh,
|
||||
iconType: poi.iconType
|
||||
},
|
||||
categories: (poi.categories || []).map(toCategory),
|
||||
positionGltf: poi.positionGltf,
|
||||
sourceConfidence: poi.sourceConfidence,
|
||||
navigationReadiness: poi.navigationReadiness,
|
||||
accessible: isPoiAccessible(poi)
|
||||
})
|
||||
123
src/data/providers/staticMuseumContentProvider.ts
Normal file
123
src/data/providers/staticMuseumContentProvider.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type {
|
||||
MuseumExhibit,
|
||||
MuseumHall
|
||||
} from '@/domain/museum'
|
||||
|
||||
export interface MuseumContentProvider {
|
||||
listExhibits(): Promise<MuseumExhibit[]>
|
||||
listHalls(): Promise<MuseumHall[]>
|
||||
}
|
||||
|
||||
const transitionalHalls: MuseumHall[] = [
|
||||
{
|
||||
id: 'hall-evolution',
|
||||
name: '演化厅',
|
||||
floorId: 'L-1',
|
||||
floorLabel: 'B1',
|
||||
description: '自然演化主题内容。当前为内容仓储过渡数据,待 CMS/API 接入后替换。',
|
||||
image: '/static/hall-placeholder.jpg',
|
||||
exhibitCount: 3,
|
||||
area: '待补充'
|
||||
},
|
||||
{
|
||||
id: 'hall-dinosaur',
|
||||
name: '恐龙厅',
|
||||
floorId: 'L2',
|
||||
floorLabel: '2F',
|
||||
description: '恐龙与古生物主题内容。当前为内容仓储过渡数据,待 CMS/API 接入后替换。',
|
||||
image: '/static/hall-placeholder.jpg',
|
||||
exhibitCount: 2,
|
||||
area: '待补充'
|
||||
},
|
||||
{
|
||||
id: 'hall-biology',
|
||||
name: '生物厅',
|
||||
floorId: 'L3',
|
||||
floorLabel: '3F',
|
||||
description: '生物多样性主题内容。当前为内容仓储过渡数据,待 CMS/API 接入后替换。',
|
||||
image: '/static/hall-placeholder.jpg',
|
||||
exhibitCount: 2,
|
||||
area: '待补充'
|
||||
}
|
||||
]
|
||||
|
||||
const transitionalExhibits: MuseumExhibit[] = [
|
||||
{
|
||||
id: 'exhibit-ginkgo-fossil',
|
||||
name: '银杏化石',
|
||||
hallId: 'hall-evolution',
|
||||
hallName: '演化厅',
|
||||
floorId: 'L-1',
|
||||
floorLabel: 'B1',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '银杏化石内容待由正式内容库补充。当前仅用于讲解业务架构迁移。',
|
||||
tags: ['化石', '古植物']
|
||||
},
|
||||
{
|
||||
id: 'exhibit-trilobite-fossil',
|
||||
name: '三叶虫化石',
|
||||
hallId: 'hall-evolution',
|
||||
hallName: '演化厅',
|
||||
floorId: 'L-1',
|
||||
floorLabel: 'B1',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '三叶虫化石内容待由正式内容库补充。当前仅用于讲解业务架构迁移。',
|
||||
tags: ['化石', '古生物']
|
||||
},
|
||||
{
|
||||
id: 'exhibit-brachiosaurus',
|
||||
name: '腕龙骨骼',
|
||||
hallId: 'hall-dinosaur',
|
||||
hallName: '恐龙厅',
|
||||
floorId: 'L2',
|
||||
floorLabel: '2F',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '腕龙骨骼内容待由正式内容库补充。当前仅用于讲解业务架构迁移。',
|
||||
tags: ['恐龙', '骨骼']
|
||||
},
|
||||
{
|
||||
id: 'exhibit-archaeopteryx',
|
||||
name: '始祖鸟标本',
|
||||
hallId: 'hall-evolution',
|
||||
hallName: '演化厅',
|
||||
floorId: 'L-1',
|
||||
floorLabel: 'B1',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '始祖鸟标本内容待由正式内容库补充。当前仅用于讲解业务架构迁移。',
|
||||
tags: ['演化', '标本']
|
||||
},
|
||||
{
|
||||
id: 'exhibit-mammoth-tusk',
|
||||
name: '猛犸象象牙',
|
||||
hallId: 'hall-biology',
|
||||
hallName: '生物厅',
|
||||
floorId: 'L3',
|
||||
floorLabel: '3F',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '猛犸象象牙内容待由正式内容库补充。当前仅用于讲解业务架构迁移。',
|
||||
tags: ['古生物', '标本']
|
||||
},
|
||||
{
|
||||
id: 'exhibit-coral-reef',
|
||||
name: '珊瑚礁标本',
|
||||
hallId: 'hall-biology',
|
||||
hallName: '生物厅',
|
||||
floorId: 'L3',
|
||||
floorLabel: '3F',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '珊瑚礁标本内容待由正式内容库补充。当前仅用于讲解业务架构迁移。',
|
||||
tags: ['海洋生物', '标本']
|
||||
}
|
||||
]
|
||||
|
||||
export class StaticMuseumContentProvider implements MuseumContentProvider {
|
||||
async listExhibits() {
|
||||
return transitionalExhibits
|
||||
}
|
||||
|
||||
async listHalls() {
|
||||
return transitionalHalls
|
||||
}
|
||||
}
|
||||
|
||||
export const staticMuseumContentProvider = new StaticMuseumContentProvider()
|
||||
214
src/data/providers/staticNavAssetsProvider.ts
Normal file
214
src/data/providers/staticNavAssetsProvider.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
import {
|
||||
NAV_ASSET_BASE_URL
|
||||
} from '@/domain/guideReadiness'
|
||||
|
||||
export interface StaticNavPoiCategoryPayload {
|
||||
topCategory: string
|
||||
topCategoryZh: string
|
||||
subcategory: string
|
||||
iconType: string
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export interface StaticNavPoiPayload {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
primaryCategory: string
|
||||
primaryCategoryZh: string
|
||||
categories?: StaticNavPoiCategoryPayload[]
|
||||
iconType?: string
|
||||
positionGltf?: [number, number, number]
|
||||
navigationReadiness?: string
|
||||
sourceConfidence?: string
|
||||
}
|
||||
|
||||
export interface StaticNavManifestFloorModelPayload {
|
||||
floorId: string
|
||||
order: number
|
||||
asset: string
|
||||
}
|
||||
|
||||
export interface StaticNavManifestPayload {
|
||||
status: string
|
||||
assets: {
|
||||
overviewModel: {
|
||||
asset: string
|
||||
}
|
||||
floorModels: StaticNavManifestFloorModelPayload[]
|
||||
}
|
||||
data: {
|
||||
floorIndex: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface StaticNavFloorIndexItemPayload {
|
||||
floorId: string
|
||||
order: number
|
||||
modelAsset: string
|
||||
poiDataAsset: string
|
||||
poiCount: number
|
||||
}
|
||||
|
||||
export interface StaticNavFloorIndexPayload {
|
||||
status: string
|
||||
floors: StaticNavFloorIndexItemPayload[]
|
||||
}
|
||||
|
||||
interface StaticFloorPoiPayload {
|
||||
status: string
|
||||
floorId: string
|
||||
pois: StaticNavPoiPayload[]
|
||||
}
|
||||
|
||||
interface StaticPoiIndexPayload {
|
||||
status: string
|
||||
pois: StaticNavPoiPayload[]
|
||||
}
|
||||
|
||||
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
|
||||
|
||||
const parseJsonPayload = <T>(payload: unknown): T => {
|
||||
if (typeof payload === 'string') {
|
||||
return JSON.parse(payload) as T
|
||||
}
|
||||
|
||||
return payload as T
|
||||
}
|
||||
|
||||
const requestJson = <T>(url: string): Promise<T> => new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
success: (response) => {
|
||||
const statusCode = Number(response.statusCode || 0)
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
reject(new Error(`导览资源读取失败: ${statusCode} ${url}`))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
resolve(parseJsonPayload<T>(response.data))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
|
||||
export interface StaticNavAssetsProvider {
|
||||
readonly baseUrl: string
|
||||
assetUrl(relativePath: string): string
|
||||
loadManifest(): Promise<StaticNavManifestPayload>
|
||||
loadFloorIndex(): Promise<StaticNavFloorIndexPayload>
|
||||
loadPoiIndex(): Promise<StaticNavPoiPayload[]>
|
||||
loadFloorPois(relativePath: string): Promise<StaticNavPoiPayload[]>
|
||||
}
|
||||
|
||||
export const createStaticNavAssetsProvider = (
|
||||
baseUrl = NAV_ASSET_BASE_URL
|
||||
): StaticNavAssetsProvider => {
|
||||
const normalizedBaseUrl = normalizeBaseUrl(baseUrl)
|
||||
let poiCache: StaticNavPoiPayload[] | null = null
|
||||
let poiRequest: Promise<StaticNavPoiPayload[]> | null = null
|
||||
let manifestCache: StaticNavManifestPayload | null = null
|
||||
let manifestRequest: Promise<StaticNavManifestPayload> | null = null
|
||||
let floorIndexCache: StaticNavFloorIndexPayload | null = null
|
||||
let floorIndexRequest: Promise<StaticNavFloorIndexPayload> | null = null
|
||||
const floorPoiCache = new Map<string, StaticNavPoiPayload[]>()
|
||||
const floorPoiRequests = new Map<string, Promise<StaticNavPoiPayload[]>>()
|
||||
|
||||
const provider: StaticNavAssetsProvider = {
|
||||
baseUrl: normalizedBaseUrl,
|
||||
assetUrl(relativePath: string) {
|
||||
return `${normalizedBaseUrl}/${relativePath.replace(/^\/+/, '')}`
|
||||
},
|
||||
async loadManifest() {
|
||||
if (manifestCache) return manifestCache
|
||||
if (manifestRequest) return manifestRequest
|
||||
|
||||
manifestRequest = requestJson<StaticNavManifestPayload>(provider.assetUrl('app_nav_manifest.json'))
|
||||
.then((data) => {
|
||||
if (data.status !== 'pass') {
|
||||
throw new Error('室内导览资源包状态不是 pass')
|
||||
}
|
||||
|
||||
manifestCache = data
|
||||
return manifestCache
|
||||
})
|
||||
.finally(() => {
|
||||
manifestRequest = null
|
||||
})
|
||||
|
||||
return manifestRequest
|
||||
},
|
||||
async loadFloorIndex() {
|
||||
if (floorIndexCache) return floorIndexCache
|
||||
if (floorIndexRequest) return floorIndexRequest
|
||||
|
||||
floorIndexRequest = provider.loadManifest()
|
||||
.then((manifest) => requestJson<StaticNavFloorIndexPayload>(provider.assetUrl(manifest.data.floorIndex)))
|
||||
.then((data) => {
|
||||
if (data.status !== 'pass') {
|
||||
throw new Error('导览楼层索引状态不是 pass')
|
||||
}
|
||||
|
||||
floorIndexCache = data
|
||||
return floorIndexCache
|
||||
})
|
||||
.finally(() => {
|
||||
floorIndexRequest = null
|
||||
})
|
||||
|
||||
return floorIndexRequest
|
||||
},
|
||||
async loadPoiIndex() {
|
||||
if (poiCache) return poiCache
|
||||
if (poiRequest) return poiRequest
|
||||
|
||||
poiRequest = requestJson<StaticPoiIndexPayload>(provider.assetUrl('data/poi_all.json'))
|
||||
.then((data) => {
|
||||
if (data.status !== 'pass') {
|
||||
throw new Error('导览 POI 数据状态不是 pass')
|
||||
}
|
||||
|
||||
poiCache = data.pois
|
||||
return poiCache
|
||||
})
|
||||
.finally(() => {
|
||||
poiRequest = null
|
||||
})
|
||||
|
||||
return poiRequest
|
||||
},
|
||||
async loadFloorPois(relativePath: string) {
|
||||
const cacheKey = relativePath.replace(/^\/+/, '')
|
||||
const cached = floorPoiCache.get(cacheKey)
|
||||
if (cached) return cached
|
||||
|
||||
const pending = floorPoiRequests.get(cacheKey)
|
||||
if (pending) return pending
|
||||
|
||||
const request = requestJson<StaticFloorPoiPayload>(provider.assetUrl(cacheKey))
|
||||
.then((data) => {
|
||||
if (data.status !== 'pass') {
|
||||
throw new Error('导览楼层 POI 数据状态不是 pass')
|
||||
}
|
||||
|
||||
floorPoiCache.set(cacheKey, data.pois)
|
||||
return data.pois
|
||||
})
|
||||
.finally(() => {
|
||||
floorPoiRequests.delete(cacheKey)
|
||||
})
|
||||
|
||||
floorPoiRequests.set(cacheKey, request)
|
||||
return request
|
||||
}
|
||||
}
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
export const defaultStaticNavAssetsProvider = createStaticNavAssetsProvider()
|
||||
26
src/domain/guideModel.ts
Normal file
26
src/domain/guideModel.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export interface GuideRenderPoi {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
primaryCategory: string
|
||||
primaryCategoryZh: string
|
||||
iconType: string
|
||||
positionGltf?: [number, number, number]
|
||||
}
|
||||
|
||||
export interface GuideModelFloorAsset {
|
||||
floorId: string
|
||||
label: string
|
||||
order: number
|
||||
modelUrl: string
|
||||
}
|
||||
|
||||
export interface GuideModelRenderPackage {
|
||||
overviewModelUrl: string
|
||||
floors: GuideModelFloorAsset[]
|
||||
}
|
||||
|
||||
export interface GuideModelSource {
|
||||
loadPackage(): Promise<GuideModelRenderPackage>
|
||||
loadFloorPois(floorId: string): Promise<GuideRenderPoi[]>
|
||||
}
|
||||
11
src/domain/guideReadiness.ts
Normal file
11
src/domain/guideReadiness.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export const NAV_ROUTE_GRAPH_READY = false
|
||||
|
||||
export const NAV_ROUTE_UNAVAILABLE_MESSAGE = '正式路线数据尚未接入,可先查看馆内三维位置'
|
||||
|
||||
export const NAV_ROUTE_READINESS = {
|
||||
ready: NAV_ROUTE_GRAPH_READY,
|
||||
message: NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||
requiredData: ['route_graph', 'nav_data']
|
||||
} as const
|
||||
|
||||
export const NAV_ASSET_BASE_URL = '/static/nav-assets/app_nav_assets_v2_clean_20260611_093623'
|
||||
123
src/domain/museum.ts
Normal file
123
src/domain/museum.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
export interface MuseumFloor {
|
||||
id: string
|
||||
label: string
|
||||
order: number
|
||||
}
|
||||
|
||||
export interface MuseumCategory {
|
||||
id: string
|
||||
label: string
|
||||
iconType?: string
|
||||
}
|
||||
|
||||
export interface MuseumPoi {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
floorLabel: string
|
||||
primaryCategory: MuseumCategory
|
||||
categories: MuseumCategory[]
|
||||
positionGltf?: [number, number, number]
|
||||
sourceConfidence?: string
|
||||
navigationReadiness?: string
|
||||
accessible: boolean
|
||||
}
|
||||
|
||||
export interface MuseumHall {
|
||||
id: string
|
||||
name: string
|
||||
floorId?: string
|
||||
floorLabel?: string
|
||||
description?: string
|
||||
image?: string
|
||||
exhibitCount?: number
|
||||
area?: string
|
||||
poiId?: string
|
||||
}
|
||||
|
||||
export interface MuseumExhibit {
|
||||
id: string
|
||||
name: string
|
||||
hallId?: string
|
||||
hallName?: string
|
||||
floorId?: string
|
||||
floorLabel?: string
|
||||
image?: string
|
||||
description?: string
|
||||
poiId?: string
|
||||
artist?: string
|
||||
year?: string
|
||||
material?: string
|
||||
size?: string
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
export interface GuideLocationPreview {
|
||||
poiId: string
|
||||
name: string
|
||||
floorId: string
|
||||
floorLabel: string
|
||||
primaryCategoryZh?: string
|
||||
positionGltf?: [number, number, number]
|
||||
}
|
||||
|
||||
export interface GuideStartLocation {
|
||||
label: string
|
||||
floor?: string
|
||||
source?: string
|
||||
}
|
||||
|
||||
export interface GuidePreviewStep {
|
||||
id: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export interface GuideLocationPreviewPlan {
|
||||
id: string
|
||||
facilityId: string
|
||||
target: string
|
||||
summary: string
|
||||
steps: GuidePreviewStep[]
|
||||
targetLocation: GuideLocationPreview | null
|
||||
startLocation: GuideStartLocation | null
|
||||
}
|
||||
|
||||
export interface GuideRouteReadiness {
|
||||
ready: boolean
|
||||
message: string
|
||||
requiredData: string[]
|
||||
}
|
||||
|
||||
export interface MediaAsset {
|
||||
id: string
|
||||
type: 'audio' | 'image' | 'transcript'
|
||||
url?: string
|
||||
duration?: number
|
||||
language?: string
|
||||
available: boolean
|
||||
unavailableReason?: string
|
||||
}
|
||||
|
||||
export interface ExplainTrack {
|
||||
id: string
|
||||
exhibitId?: string
|
||||
hallId?: string
|
||||
title: string
|
||||
summary?: string
|
||||
mediaId?: string
|
||||
coverImage?: string
|
||||
poiId?: string
|
||||
floorId?: string
|
||||
available: boolean
|
||||
}
|
||||
|
||||
export interface SearchIndexItem {
|
||||
id: string
|
||||
name: string
|
||||
desc: string
|
||||
type: 'exhibit' | 'hall' | 'facility'
|
||||
poiId?: string
|
||||
floorId?: string
|
||||
exhibitId?: string
|
||||
hallId?: string
|
||||
}
|
||||
@@ -50,7 +50,7 @@
|
||||
<view class="action-btn-group">
|
||||
<view class="action-btn" @tap="handleNavigate">
|
||||
<text class="btn-icon">🗺️</text>
|
||||
<text class="btn-text">导航</text>
|
||||
<text class="btn-text">查看位置</text>
|
||||
</view>
|
||||
<view class="action-btn" @tap="handleCollect">
|
||||
<text class="btn-icon">{{ isCollected ? '❤️' : '🤍' }}</text>
|
||||
@@ -70,32 +70,39 @@
|
||||
import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import {
|
||||
toExplainDetailViewModel,
|
||||
type ExplainDetailViewModel
|
||||
} from '@/view-models/explainViewModels'
|
||||
import {
|
||||
isGuideTopTab,
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
|
||||
const exhibit = ref({
|
||||
id: '1',
|
||||
name: '蒙娜丽莎',
|
||||
artist: '列奥纳多·达·芬奇',
|
||||
year: '1503-1519',
|
||||
material: '木板油画',
|
||||
size: '77cm × 53cm',
|
||||
hall: '1号展厅 1F',
|
||||
const exhibit = ref<ExplainDetailViewModel>({
|
||||
id: '',
|
||||
name: '展项内容',
|
||||
hall: '展厅位置待补充',
|
||||
image: '/static/exhibit-placeholder.jpg',
|
||||
description: '《蒙娜丽莎》是意大利文艺复兴时期画家列奥纳多·达·芬奇创作的油画,现收藏于法国卢浮宫博物馆。该画作主要表现了女性的典雅和恬静的典型形象,塑造了资本主义上升时期一位城市有产阶级的妇女形象。'
|
||||
description: '该展项介绍待正式内容库补充。',
|
||||
hasAudio: false,
|
||||
audioUnavailableReason: '正式讲解音频尚未接入媒体仓储'
|
||||
})
|
||||
|
||||
const isPlaying = ref(false)
|
||||
const isCollected = ref(false)
|
||||
const activeTopTab = ref<GuideTopTab>('guide')
|
||||
|
||||
onLoad((options: any) => {
|
||||
onLoad(async (options: any) => {
|
||||
if (options.id) {
|
||||
// 根据 ID 加载展品数据
|
||||
console.log('加载展品:', options.id)
|
||||
const exhibitData = await explainUseCase.getExhibitById(options.id)
|
||||
if (exhibitData) {
|
||||
exhibit.value = toExplainDetailViewModel(exhibitData)
|
||||
}
|
||||
}
|
||||
|
||||
const tab = Array.isArray(options.tab) ? options.tab[0] : options.tab
|
||||
@@ -104,14 +111,29 @@ onLoad((options: any) => {
|
||||
}
|
||||
})
|
||||
|
||||
const handlePlayAudio = () => {
|
||||
isPlaying.value = !isPlaying.value
|
||||
console.log('播放/暂停音频')
|
||||
const handlePlayAudio = async () => {
|
||||
const selection = exhibit.value.id
|
||||
? await explainUseCase.selectAudioForExhibit(exhibit.value.id)
|
||||
: null
|
||||
|
||||
uni.showToast({
|
||||
title: selection?.unavailableMessage || exhibit.value.audioUnavailableReason || '该展项暂无讲解音频',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
const handleNavigate = () => {
|
||||
console.log('导航到展品位置')
|
||||
uni.navigateBack()
|
||||
if (!exhibit.value.poiId) {
|
||||
uni.showToast({
|
||||
title: '该展项暂无三维位置数据',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/route/detail?facilityId=${encodeURIComponent(exhibit.value.poiId)}&target=${encodeURIComponent(exhibit.value.name)}&state=preview`
|
||||
})
|
||||
}
|
||||
|
||||
const handleCollect = () => {
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
floor-top="208px"
|
||||
tools-top="450px"
|
||||
:tools="['回正', '2D']"
|
||||
:indoor-model-source="indoorModelSource"
|
||||
:floors="guideFloors"
|
||||
:normalize-floor-id="normalizeGuideFloorId"
|
||||
@search-tap="handleSearchTap"
|
||||
@mode-change="handleModeChange"
|
||||
@floor-change="handleFloorChange"
|
||||
@@ -115,16 +118,19 @@ import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||
import {
|
||||
NAV_FLOOR_OPTIONS,
|
||||
NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||
formatNavFloorLabel,
|
||||
isPoiAccessible,
|
||||
loadCleanNavPoiById
|
||||
} from '@/services/navAssets'
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
toFacilityDetailViewModel,
|
||||
type FacilityDetailViewModel
|
||||
} from '@/view-models/guideViewModels'
|
||||
import {
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
import type {
|
||||
MuseumFloor
|
||||
} from '@/domain/museum'
|
||||
|
||||
interface ConfirmedStart {
|
||||
label: string
|
||||
@@ -132,19 +138,23 @@ interface ConfirmedStart {
|
||||
source: 'facility-detail'
|
||||
}
|
||||
|
||||
const facility = ref({
|
||||
const indoorModelSource = guideUseCase.getModelSource()
|
||||
const guideFloors = ref<MuseumFloor[]>([])
|
||||
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
|
||||
|
||||
const facility = ref<FacilityDetailViewModel>({
|
||||
id: '',
|
||||
name: '目标位置',
|
||||
status: '可预览',
|
||||
location: 'clean 导览数据点位',
|
||||
traffic: NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||
traffic: guideUseCase.getRouteReadiness().message,
|
||||
tags: ['三维位置', 'clean 数据']
|
||||
})
|
||||
const isStartPanelOpen = ref(false)
|
||||
const selectedStartFloor = ref('1F')
|
||||
const selectedStartArea = ref('服务台附近')
|
||||
const confirmedStart = ref<ConfirmedStart | null>(null)
|
||||
const startFloors = NAV_FLOOR_OPTIONS.map((floor) => floor.label)
|
||||
const startFloors = computed(() => guideFloors.value.map((floor) => floor.label))
|
||||
const startAreas = ['主入口', '服务台附近', '停车场入口']
|
||||
|
||||
const pendingStartLabel = computed(() => `${selectedStartFloor.value} ${selectedStartArea.value}`)
|
||||
@@ -165,7 +175,23 @@ const syncDraftStartSelection = () => {
|
||||
selectedStartArea.value = resolveConfirmedStartArea()
|
||||
}
|
||||
|
||||
const loadGuideFloors = async () => {
|
||||
try {
|
||||
const floors = await guideUseCase.getFloors()
|
||||
guideFloors.value = floors
|
||||
selectedStartFloor.value = floors.find((floor) => floor.label === selectedStartFloor.value)?.label
|
||||
|| floors.find((floor) => floor.id === 'L1')?.label
|
||||
|| floors[0]?.label
|
||||
|| selectedStartFloor.value
|
||||
} catch (error) {
|
||||
console.error('加载导览楼层失败:', error)
|
||||
guideFloors.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
void loadGuideFloors()
|
||||
|
||||
if (options.id) {
|
||||
facility.value.id = options.id
|
||||
}
|
||||
@@ -177,22 +203,13 @@ onLoad(async (options: any) => {
|
||||
if (!facility.value.id) return
|
||||
|
||||
try {
|
||||
const poi = await loadCleanNavPoiById(facility.value.id)
|
||||
const poi = await guideUseCase.getPoiById(facility.value.id)
|
||||
if (!poi) return
|
||||
|
||||
const floor = formatNavFloorLabel(poi.floorId)
|
||||
facility.value = {
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
status: '可预览',
|
||||
location: `${floor} · ${poi.primaryCategoryZh}`,
|
||||
traffic: NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||
tags: [
|
||||
floor,
|
||||
poi.primaryCategoryZh,
|
||||
isPoiAccessible(poi) ? '无障碍相关' : '展示点位'
|
||||
]
|
||||
}
|
||||
facility.value = toFacilityDetailViewModel(
|
||||
poi,
|
||||
guideUseCase.getRouteReadiness().message
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('加载 clean 设施详情失败:', error)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<view class="hall-hero">
|
||||
<image class="hero-image" :src="hall.image" mode="aspectFill" />
|
||||
<view class="hall-badge">
|
||||
<text class="badge-text">{{ hall.floor }}</text>
|
||||
<text class="badge-text">{{ hall.floorLabel }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="action-bar">
|
||||
<view class="action-btn primary" @tap="handleNavigate">
|
||||
<text class="btn-text">导航到展厅</text>
|
||||
<text class="btn-text">查看展厅位置</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -60,6 +60,15 @@ import { ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import ExhibitCard from '@/components/content/ExhibitCard.vue'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import {
|
||||
toExplainExhibitViewModel,
|
||||
toHallDetailViewModel,
|
||||
type ExplainExhibitViewModel,
|
||||
type HallDetailViewModel
|
||||
} from '@/view-models/explainViewModels'
|
||||
import {
|
||||
isGuideTopTab,
|
||||
navigateToGuideTopTab,
|
||||
@@ -68,24 +77,26 @@ import {
|
||||
|
||||
const activeTopTab = ref<GuideTopTab>('guide')
|
||||
|
||||
const hall = ref({
|
||||
id: '1',
|
||||
name: '1号展厅',
|
||||
floor: '1F',
|
||||
description: '文艺复兴时期艺术作品展厅,展示了欧洲文艺复兴时期最具代表性的绘画和雕塑作品。',
|
||||
const hall = ref<HallDetailViewModel>({
|
||||
id: '',
|
||||
name: '展厅内容',
|
||||
floorLabel: '楼层待补充',
|
||||
description: '该展厅介绍待正式内容库补充。',
|
||||
image: '/static/hall-placeholder.jpg',
|
||||
exhibitCount: 25,
|
||||
area: '500㎡'
|
||||
exhibitCount: 0,
|
||||
area: '待补充'
|
||||
})
|
||||
|
||||
const exhibits = ref([
|
||||
{ id: '1', name: '蒙娜丽莎', artist: '达芬奇', hasAudio: true },
|
||||
{ id: '2', name: '最后的晚餐', artist: '达芬奇', hasAudio: true }
|
||||
])
|
||||
const exhibits = ref<ExplainExhibitViewModel[]>([])
|
||||
|
||||
onLoad((options: any) => {
|
||||
onLoad(async (options: any) => {
|
||||
if (options.id) {
|
||||
console.log('加载展厅:', options.id)
|
||||
const hallData = await explainUseCase.getHallById(options.id)
|
||||
if (hallData) {
|
||||
hall.value = toHallDetailViewModel(hallData)
|
||||
const hallExhibits = await explainUseCase.listExhibitsByHallId(hallData.id)
|
||||
exhibits.value = hallExhibits.map(toExplainExhibitViewModel)
|
||||
}
|
||||
}
|
||||
|
||||
const tab = Array.isArray(options.tab) ? options.tab[0] : options.tab
|
||||
@@ -101,8 +112,17 @@ const handleExhibitClick = (exhibit: any) => {
|
||||
}
|
||||
|
||||
const handleNavigate = () => {
|
||||
console.log('导航到展厅')
|
||||
uni.navigateBack()
|
||||
if (!hall.value.poiId) {
|
||||
uni.showToast({
|
||||
title: '该展厅暂无三维位置数据',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/pages/route/detail?facilityId=${encodeURIComponent(hall.value.poiId)}&target=${encodeURIComponent(hall.value.name)}&state=preview`
|
||||
})
|
||||
}
|
||||
|
||||
const handleTopTabChange = (tab: GuideTopTab) => {
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
:map-type="guideMapType"
|
||||
:outdoor-variant="guideOutdoorVariant"
|
||||
:indoor-asset-base-url="indoorNavAssetBaseUrl"
|
||||
:indoor-model-source="indoorModelSource"
|
||||
:floors="guideFloors"
|
||||
:normalize-floor-id="normalizeGuideFloorId"
|
||||
indoor-initial-view="overview"
|
||||
:indoor-view="indoorView"
|
||||
:active-floor="activeGuideFloor"
|
||||
@@ -80,6 +83,7 @@
|
||||
|
||||
<view v-else-if="currentTab === 'explain'" class="explain-page">
|
||||
<ExplainList
|
||||
:exhibits="explainExhibits"
|
||||
@exhibit-click="handleExplainExhibitClick"
|
||||
@featured-click="handleExplainFeaturedClick"
|
||||
@audio-click="handleAudioClick"
|
||||
@@ -109,7 +113,7 @@ import { computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||
import ExplainList from '@/components/explain/ExplainList.vue'
|
||||
import ExplainList, { type Exhibit } from '@/components/explain/ExplainList.vue'
|
||||
import FloatingAudioButton from '@/components/audio/FloatingAudioButton.vue'
|
||||
import AudioPlayer, { type AudioItem } from '@/components/audio/AudioPlayer.vue'
|
||||
import {
|
||||
@@ -117,9 +121,20 @@ import {
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
import {
|
||||
NAV_ASSET_BASE_URL,
|
||||
NAV_ROUTE_UNAVAILABLE_MESSAGE
|
||||
} from '@/services/navAssets'
|
||||
} from '@/domain/guideReadiness'
|
||||
import {
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
explainUseCase
|
||||
} from '@/usecases/explainUseCase'
|
||||
import {
|
||||
toExplainExhibitViewModel
|
||||
} from '@/view-models/explainViewModels'
|
||||
import type {
|
||||
MuseumFloor
|
||||
} from '@/domain/museum'
|
||||
|
||||
// 3D 模式状态
|
||||
const is3DMode = ref(false)
|
||||
@@ -135,7 +150,10 @@ const showMarkerDetail = ref(false)
|
||||
const showAreaSelector = ref(false)
|
||||
const selectedAreaId = ref('service')
|
||||
|
||||
const indoorNavAssetBaseUrl = NAV_ASSET_BASE_URL
|
||||
const indoorNavAssetBaseUrl = guideUseCase.getAssetBaseUrl()
|
||||
const indoorModelSource = guideUseCase.getModelSource()
|
||||
const guideFloors = ref<MuseumFloor[]>([])
|
||||
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
|
||||
|
||||
const shortcutItems = [
|
||||
{ id: 'toilet', name: '卫生间', abbr: '卫' },
|
||||
@@ -149,6 +167,7 @@ const shortcutItems = [
|
||||
const showAudioPlayer = ref(false)
|
||||
const isAudioPlaying = ref(false)
|
||||
const currentAudio = ref<AudioItem | null>(null)
|
||||
const explainExhibits = ref<Exhibit[]>([])
|
||||
|
||||
// 选中的标记详情
|
||||
interface MarkerDetail {
|
||||
@@ -188,8 +207,35 @@ onLoad((options: any = {}) => {
|
||||
if (isGuideTopTab(tab)) {
|
||||
currentTab.value = tab
|
||||
}
|
||||
|
||||
void loadGuideFloors()
|
||||
void loadExplainExhibits()
|
||||
})
|
||||
|
||||
const loadGuideFloors = async () => {
|
||||
try {
|
||||
const floors = await guideUseCase.getFloors()
|
||||
guideFloors.value = floors
|
||||
activeGuideFloor.value = floors.find((floor) => floor.label === activeGuideFloor.value)?.label
|
||||
|| floors.find((floor) => floor.id === 'L1')?.label
|
||||
|| floors[0]?.label
|
||||
|| activeGuideFloor.value
|
||||
} catch (error) {
|
||||
console.error('加载导览楼层失败:', error)
|
||||
guideFloors.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const loadExplainExhibits = async () => {
|
||||
try {
|
||||
const exhibits = await explainUseCase.listExhibits()
|
||||
explainExhibits.value = exhibits.map(toExplainExhibitViewModel)
|
||||
} catch (error) {
|
||||
console.error('加载讲解内容失败:', error)
|
||||
explainExhibits.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索处理
|
||||
const handleSearchFocus = () => {
|
||||
searchBarFocused.value = true
|
||||
@@ -391,13 +437,23 @@ const guideSearchText = computed(() => {
|
||||
})
|
||||
|
||||
// 音频相关处理
|
||||
const handleAudioClick = (exhibit: any) => {
|
||||
console.log('点击音频播放:', exhibit)
|
||||
const handleAudioClick = async (exhibit: Exhibit) => {
|
||||
const selection = await explainUseCase.selectAudioForExhibit(exhibit.id)
|
||||
|
||||
if (!selection?.playable || !selection.media?.url) {
|
||||
uni.showToast({
|
||||
title: selection?.unavailableMessage || exhibit.audioUnavailableReason || '该展项暂无讲解音频',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
currentAudio.value = {
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3',
|
||||
image: exhibit.image
|
||||
id: selection.media.id,
|
||||
name: selection.track?.title || selection.exhibit.name,
|
||||
audioUrl: selection.media.url,
|
||||
image: selection.exhibit.image,
|
||||
duration: selection.media.duration
|
||||
}
|
||||
showAudioPlayer.value = true
|
||||
}
|
||||
@@ -431,30 +487,14 @@ const handleAudioEnded = (audio: AudioItem) => {
|
||||
}
|
||||
|
||||
// 讲解页面处理
|
||||
const handleExplainExhibitClick = (exhibit: any) => {
|
||||
console.log('点击讲解展品:', exhibit)
|
||||
const handleExplainExhibitClick = (exhibit: Exhibit) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/exhibit/detail?id=${exhibit.id}&tab=explain`
|
||||
})
|
||||
}
|
||||
|
||||
const handleExplainFeaturedClick = (exhibit: any) => {
|
||||
console.log('点击推荐讲解:', exhibit)
|
||||
// 播放音频讲解
|
||||
if (exhibit.hasAudio) {
|
||||
currentAudio.value = {
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
audioUrl: exhibit.audioUrl || 'https://example.com/audio.mp3',
|
||||
image: exhibit.image
|
||||
}
|
||||
showAudioPlayer.value = true
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '该展品暂无讲解音频',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
const handleExplainFeaturedClick = async (exhibit: Exhibit) => {
|
||||
await handleAudioClick(exhibit)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
>
|
||||
<GuideMapShell
|
||||
v-bind="shellConfig"
|
||||
:indoor-model-source="indoorModelSource"
|
||||
:floors="guideFloors"
|
||||
:normalize-floor-id="normalizeGuideFloorId"
|
||||
:target-focus-request="targetFocusRequest"
|
||||
@search-tap="handleSearchTap"
|
||||
@mode-change="handleModeChange"
|
||||
@@ -125,12 +128,18 @@ import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||
import LocationPreview from '@/components/navigation/LocationPreview.vue'
|
||||
import {
|
||||
NAV_FLOOR_OPTIONS,
|
||||
NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||
formatNavFloorLabel,
|
||||
loadCleanNavPoiById,
|
||||
type CleanNavPoi
|
||||
} from '@/services/navAssets'
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
initialLocationPreviewPlan,
|
||||
toTargetFocusRequestViewModel
|
||||
} from '@/view-models/guideViewModels'
|
||||
import type {
|
||||
GuideLocationPreview,
|
||||
GuideLocationPreviewPlan,
|
||||
GuideStartLocation,
|
||||
MuseumFloor
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
@@ -138,28 +147,11 @@ import {
|
||||
|
||||
type RouteViewState = 'preview' | 'location-error' | 'outdoor-preview'
|
||||
type GuideMode = '2d' | '3d'
|
||||
interface RouteStep {
|
||||
id: string
|
||||
text: string
|
||||
}
|
||||
|
||||
interface RouteTargetLocation {
|
||||
poiId: string
|
||||
name: string
|
||||
floorId: string
|
||||
floorLabel?: string
|
||||
primaryCategoryZh?: string
|
||||
positionGltf?: [number, number, number]
|
||||
}
|
||||
|
||||
interface RouteStartLocation {
|
||||
label: string
|
||||
floor?: string
|
||||
source?: string
|
||||
}
|
||||
type RouteTargetLocation = GuideLocationPreview
|
||||
type RouteStartLocation = GuideStartLocation
|
||||
|
||||
interface TargetPoiFocusRequest extends RouteTargetLocation {
|
||||
requestId: number
|
||||
requestId: number | string
|
||||
}
|
||||
|
||||
interface TargetPoiFocusResult {
|
||||
@@ -170,104 +162,44 @@ interface TargetPoiFocusResult {
|
||||
message?: string
|
||||
}
|
||||
|
||||
interface RoutePlan {
|
||||
id: string
|
||||
facilityId: string
|
||||
target: string
|
||||
summary: string
|
||||
steps: RouteStep[]
|
||||
targetLocation: RouteTargetLocation | null
|
||||
startLocation: RouteStartLocation | null
|
||||
}
|
||||
type RoutePlan = GuideLocationPreviewPlan
|
||||
|
||||
const routeDeepLinkStates: RouteViewState[] = ['preview', 'outdoor-preview']
|
||||
const indoorModelSource = guideUseCase.getModelSource()
|
||||
const guideFloors = ref<MuseumFloor[]>([])
|
||||
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
|
||||
|
||||
const startSourceLabels: Record<string, string> = {
|
||||
'facility-detail': '设施详情选择',
|
||||
manual: '手动选择'
|
||||
}
|
||||
|
||||
const formatStartSourceLabel = (source?: string) => (
|
||||
source ? startSourceLabels[source] || source : ''
|
||||
)
|
||||
|
||||
const formatStartStepText = (startLocation: RouteStartLocation) => {
|
||||
const sourceLabel = formatStartSourceLabel(startLocation.source)
|
||||
const floorSuffix = startLocation.floor && !startLocation.label.includes(startLocation.floor)
|
||||
? ` · ${startLocation.floor}`
|
||||
: ''
|
||||
const sourceSuffix = sourceLabel ? `(${sourceLabel})` : ''
|
||||
|
||||
return `起点:${startLocation.label}${floorSuffix}${sourceSuffix}`
|
||||
}
|
||||
|
||||
const createPreviewSteps = (
|
||||
poi: CleanNavPoi | null,
|
||||
startLocation: RouteStartLocation | null
|
||||
): RouteStep[] => {
|
||||
if (startLocation) {
|
||||
return [
|
||||
{
|
||||
id: 'start',
|
||||
text: formatStartStepText(startLocation)
|
||||
},
|
||||
{
|
||||
id: 'target',
|
||||
text: poi
|
||||
? `目标:${formatNavFloorLabel(poi.floorId)} · ${poi.primaryCategoryZh} · 位置预览`
|
||||
: '目标来自 clean 导览数据,暂仅支持位置预览'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'floor',
|
||||
text: poi ? `目标位于 ${formatNavFloorLabel(poi.floorId)} · ${poi.primaryCategoryZh}` : '目标来自 clean 导览数据'
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
text: NAV_ROUTE_UNAVAILABLE_MESSAGE
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const createRoutePlan = (
|
||||
facilityId: string,
|
||||
target: string,
|
||||
poi: CleanNavPoi | null = null,
|
||||
startLocation: RouteStartLocation | null = null
|
||||
): RoutePlan => ({
|
||||
id: `route-${facilityId || 'custom'}`,
|
||||
facilityId,
|
||||
target,
|
||||
summary: poi
|
||||
? `${formatNavFloorLabel(poi.floorId)} · ${poi.primaryCategoryZh} · 位置预览`
|
||||
: '位置预览 · 尚未接入正式路线图',
|
||||
steps: createPreviewSteps(poi, startLocation),
|
||||
targetLocation: poi ? {
|
||||
poiId: poi.id,
|
||||
name: poi.name,
|
||||
floorId: poi.floorId,
|
||||
floorLabel: formatNavFloorLabel(poi.floorId),
|
||||
primaryCategoryZh: poi.primaryCategoryZh,
|
||||
positionGltf: poi.positionGltf
|
||||
} : null,
|
||||
startLocation
|
||||
})
|
||||
|
||||
const route = ref<RoutePlan>(createRoutePlan('', '目标地点'))
|
||||
const route = ref<RoutePlan>(initialLocationPreviewPlan())
|
||||
const hasRouteTarget = ref(false)
|
||||
const routeViewState = ref<RouteViewState>('preview')
|
||||
const activeFloor = ref('1F')
|
||||
const targetFocusRequest = ref<TargetPoiFocusRequest | null>(null)
|
||||
const targetFocusRequestId = ref(0)
|
||||
const manualFloors = NAV_FLOOR_OPTIONS.map((floor) => floor.label)
|
||||
const manualFloors = computed(() => guideFloors.value.map((floor) => floor.label))
|
||||
const guideFloorLabel = (floorId: string) => (
|
||||
guideFloors.value.find((floor) => floor.id === floorId)?.label || floorId
|
||||
)
|
||||
const defaultGuideFloorLabel = () => (
|
||||
guideFloors.value.find((floor) => floor.id === 'L1')?.label
|
||||
|| guideFloors.value[0]?.label
|
||||
|| '1F'
|
||||
)
|
||||
const loadGuideFloors = async () => {
|
||||
if (guideFloors.value.length) return guideFloors.value
|
||||
|
||||
try {
|
||||
guideFloors.value = await guideUseCase.getFloors()
|
||||
} catch (error) {
|
||||
console.error('加载导览楼层失败:', error)
|
||||
guideFloors.value = []
|
||||
}
|
||||
|
||||
return guideFloors.value
|
||||
}
|
||||
const manualAreas = ['主入口', '服务台附近', '停车场入口']
|
||||
const isManualLocationPanelOpen = ref(false)
|
||||
const selectedManualFloor = ref('1F')
|
||||
const selectedManualArea = ref('服务台附近')
|
||||
|
||||
const normalizeRouteOption = (value: unknown) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value[0]
|
||||
@@ -380,7 +312,36 @@ const createStartLocationFromOptions = (options: Record<string, unknown>) => {
|
||||
}
|
||||
}
|
||||
|
||||
const requestTargetFocus = (target: Partial<RouteTargetLocation> | null, fallbackName = route.value.target) => {
|
||||
if (!target?.poiId || !target.floorId) {
|
||||
targetFocusRequest.value = null
|
||||
return false
|
||||
}
|
||||
|
||||
const floorLabel = target.floorLabel || guideFloorLabel(target.floorId)
|
||||
const focusTarget: RouteTargetLocation = {
|
||||
poiId: target.poiId,
|
||||
name: target.name || fallbackName,
|
||||
floorId: target.floorId,
|
||||
floorLabel,
|
||||
primaryCategoryZh: target.primaryCategoryZh,
|
||||
positionGltf: target.positionGltf
|
||||
}
|
||||
|
||||
activeFloor.value = floorLabel
|
||||
routeViewState.value = 'preview'
|
||||
targetFocusRequestId.value += 1
|
||||
targetFocusRequest.value = toTargetFocusRequestViewModel(
|
||||
focusTarget,
|
||||
targetFocusRequestId.value
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const applyRouteOptions = async (options: Record<string, unknown> = {}) => {
|
||||
await loadGuideFloors()
|
||||
|
||||
const facilityId = normalizeStringOption(options.facilityId)
|
||||
const target = normalizeStringOption(options.target)
|
||||
const state = normalizeRouteOption(options.state)
|
||||
@@ -388,39 +349,42 @@ const applyRouteOptions = async (options: Record<string, unknown> = {}) => {
|
||||
|
||||
if (!facilityId && !target) {
|
||||
hasRouteTarget.value = false
|
||||
route.value = createRoutePlan('', '目标地点')
|
||||
route.value = initialLocationPreviewPlan()
|
||||
routeViewState.value = 'preview'
|
||||
activeFloor.value = '1F'
|
||||
activeFloor.value = defaultGuideFloorLabel()
|
||||
targetFocusRequest.value = null
|
||||
isManualLocationPanelOpen.value = false
|
||||
return false
|
||||
}
|
||||
|
||||
const matchedPoi = facilityId ? await loadCleanNavPoiById(facilityId) : null
|
||||
const matchedPoi = facilityId ? await guideUseCase.getPoiById(facilityId) : null
|
||||
|
||||
if (!matchedPoi) {
|
||||
hasRouteTarget.value = false
|
||||
route.value = createRoutePlan('', target || '目标地点')
|
||||
route.value = await guideUseCase.createLocationPreviewPlan('', target || '目标地点', startLocation)
|
||||
routeViewState.value = 'preview'
|
||||
activeFloor.value = startLocation?.floor || '1F'
|
||||
activeFloor.value = startLocation?.floor || defaultGuideFloorLabel()
|
||||
targetFocusRequest.value = null
|
||||
isManualLocationPanelOpen.value = false
|
||||
return false
|
||||
}
|
||||
|
||||
route.value = createRoutePlan(
|
||||
route.value = await guideUseCase.createLocationPreviewPlan(
|
||||
facilityId,
|
||||
target || matchedPoi.name,
|
||||
matchedPoi,
|
||||
startLocation
|
||||
)
|
||||
|
||||
hasRouteTarget.value = true
|
||||
routeViewState.value = isRouteViewState(state) ? state : 'preview'
|
||||
activeFloor.value = matchedPoi ? formatNavFloorLabel(matchedPoi.floorId) : startLocation?.floor || '1F'
|
||||
targetFocusRequest.value = null
|
||||
isManualLocationPanelOpen.value = false
|
||||
|
||||
if (routeViewState.value === 'preview') {
|
||||
requestTargetFocus(route.value.targetLocation, matchedPoi.name)
|
||||
} else {
|
||||
activeFloor.value = matchedPoi.floorLabel || guideFloorLabel(matchedPoi.floorId)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -479,23 +443,14 @@ const handleViewOutdoorMap = () => {
|
||||
routeViewState.value = 'outdoor-preview'
|
||||
}
|
||||
|
||||
const handleShowTargetLocation = (target: RouteTargetLocation | null) => {
|
||||
const targetLocation = target || route.value.targetLocation
|
||||
const handleShowTargetLocation = (target: Partial<RouteTargetLocation> | null) => {
|
||||
const focused = requestTargetFocus(target || route.value.targetLocation)
|
||||
|
||||
if (!targetLocation?.poiId || !targetLocation.floorId) {
|
||||
if (!focused) {
|
||||
uni.showToast({
|
||||
title: '目标暂无三维位置数据',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
routeViewState.value = 'preview'
|
||||
activeFloor.value = targetLocation.floorLabel || formatNavFloorLabel(targetLocation.floorId)
|
||||
targetFocusRequestId.value += 1
|
||||
targetFocusRequest.value = {
|
||||
...targetLocation,
|
||||
requestId: targetFocusRequestId.value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,6 +466,7 @@ const handleTargetFocus = (result: TargetPoiFocusResult) => {
|
||||
const handleReturnToPreview = () => {
|
||||
isManualLocationPanelOpen.value = false
|
||||
routeViewState.value = 'preview'
|
||||
requestTargetFocus(route.value.targetLocation)
|
||||
}
|
||||
|
||||
const handleRelocate = () => {
|
||||
@@ -540,39 +496,24 @@ const createManualStartLocation = (): RouteStartLocation => ({
|
||||
source: 'manual'
|
||||
})
|
||||
|
||||
const createCurrentTargetStep = (): RouteStep => {
|
||||
const targetLocation = route.value.targetLocation
|
||||
|
||||
return {
|
||||
id: 'target',
|
||||
text: targetLocation
|
||||
? `目标:${targetLocation.floorLabel || formatNavFloorLabel(targetLocation.floorId)} · ${targetLocation.primaryCategoryZh || route.value.target} · 位置预览`
|
||||
: '目标来自 clean 导览数据,暂仅支持位置预览'
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirmLocation = () => {
|
||||
const handleConfirmLocation = async () => {
|
||||
const startLocation = createManualStartLocation()
|
||||
|
||||
route.value = {
|
||||
...route.value,
|
||||
startLocation,
|
||||
steps: [
|
||||
{
|
||||
id: 'start',
|
||||
text: formatStartStepText(startLocation)
|
||||
},
|
||||
createCurrentTargetStep()
|
||||
]
|
||||
}
|
||||
route.value = await guideUseCase.createLocationPreviewPlan(
|
||||
route.value.facilityId,
|
||||
route.value.target,
|
||||
startLocation
|
||||
)
|
||||
activeFloor.value = startLocation.floor || activeFloor.value
|
||||
isManualLocationPanelOpen.value = false
|
||||
routeViewState.value = 'preview'
|
||||
requestTargetFocus(route.value.targetLocation)
|
||||
}
|
||||
|
||||
const handleModeChange = (mode: GuideMode) => {
|
||||
if (routeViewState.value === 'outdoor-preview' && mode === '3d') {
|
||||
routeViewState.value = 'preview'
|
||||
requestTargetFocus(route.value.targetLocation)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -596,7 +537,7 @@ const handleToolClick = (tool: string) => {
|
||||
}
|
||||
|
||||
uni.showToast({
|
||||
title: tool === '回正' ? '已回到三维总览' : NAV_ROUTE_UNAVAILABLE_MESSAGE,
|
||||
title: tool === '回正' ? '已回到三维总览' : guideUseCase.getRouteReadiness().message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
search-top="60px"
|
||||
mode-top="104px"
|
||||
floor-top="208px"
|
||||
:indoor-model-source="indoorModelSource"
|
||||
:floors="guideFloors"
|
||||
:normalize-floor-id="normalizeGuideFloorId"
|
||||
@search-tap="handleSearchTap"
|
||||
@mode-change="handleModeChange"
|
||||
@floor-change="handleFloorChange"
|
||||
@@ -100,31 +103,31 @@ import { onLoad } from '@dcloudio/uni-app'
|
||||
import GuidePageFrame from '@/components/navigation/GuidePageFrame.vue'
|
||||
import GuideMapShell from '@/components/navigation/GuideMapShell.vue'
|
||||
import {
|
||||
formatNavFloorLabel,
|
||||
isPoiAccessible,
|
||||
searchCleanNavPois,
|
||||
type CleanNavPoi
|
||||
} from '@/services/navAssets'
|
||||
guideUseCase
|
||||
} from '@/usecases/guideUseCase'
|
||||
import {
|
||||
toFacilityResultViewModel,
|
||||
type FacilityResultViewModel
|
||||
} from '@/view-models/guideViewModels'
|
||||
import {
|
||||
navigateToGuideTopTab,
|
||||
type GuideTopTab
|
||||
} from '@/utils/guideTopTabs'
|
||||
import type {
|
||||
MuseumFloor
|
||||
} from '@/domain/museum'
|
||||
|
||||
interface FilterItem {
|
||||
id: 'all' | 'accessible' | 'floor'
|
||||
label: string
|
||||
}
|
||||
|
||||
interface FacilityResult {
|
||||
id: string
|
||||
name: string
|
||||
floor: string
|
||||
meta: string
|
||||
accessible: boolean
|
||||
action: '查看'
|
||||
}
|
||||
interface FacilityResult extends FacilityResultViewModel {}
|
||||
|
||||
const searchKeyword = ref('卫生间')
|
||||
const indoorModelSource = guideUseCase.getModelSource()
|
||||
const guideFloors = ref<MuseumFloor[]>([])
|
||||
const normalizeGuideFloorId = (labelOrId: string) => guideUseCase.normalizeFloorId(labelOrId)
|
||||
const searchDraftKeyword = ref('')
|
||||
const currentFilter = ref<FilterItem['id']>('all')
|
||||
const isLoading = ref(false)
|
||||
@@ -140,18 +143,6 @@ const filters: FilterItem[] = [
|
||||
|
||||
const facilities = ref<FacilityResult[]>([])
|
||||
|
||||
const toFacilityResult = (poi: CleanNavPoi): FacilityResult => {
|
||||
const floor = formatNavFloorLabel(poi.floorId)
|
||||
return {
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
floor,
|
||||
meta: `${floor}|${poi.primaryCategoryZh}|${poi.navigationReadiness || '展示点位'}`,
|
||||
accessible: isPoiAccessible(poi),
|
||||
action: '查看'
|
||||
}
|
||||
}
|
||||
|
||||
const visibleFacilities = computed(() => {
|
||||
if (currentFilter.value === 'accessible') {
|
||||
return facilities.value.filter((item) => item.accessible)
|
||||
@@ -170,10 +161,10 @@ const resultTitle = computed(() => {
|
||||
const loadFacilityResults = async () => {
|
||||
isLoading.value = true
|
||||
try {
|
||||
const pois = await searchCleanNavPois(searchKeyword.value)
|
||||
const pois = await guideUseCase.searchPois(searchKeyword.value)
|
||||
facilities.value = pois
|
||||
.filter((poi) => poi.primaryCategory !== 'touring_poi')
|
||||
.map(toFacilityResult)
|
||||
.filter((poi) => poi.primaryCategory.id !== 'touring_poi')
|
||||
.map(toFacilityResultViewModel)
|
||||
} catch (error) {
|
||||
console.error('加载 clean 导览搜索结果失败:', error)
|
||||
facilities.value = []
|
||||
@@ -186,11 +177,21 @@ const loadFacilityResults = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadGuideFloors = async () => {
|
||||
try {
|
||||
guideFloors.value = await guideUseCase.getFloors()
|
||||
} catch (error) {
|
||||
console.error('加载导览楼层失败:', error)
|
||||
guideFloors.value = []
|
||||
}
|
||||
}
|
||||
|
||||
onLoad((options: any) => {
|
||||
if (options.keyword) {
|
||||
searchKeyword.value = decodeURIComponent(options.keyword)
|
||||
}
|
||||
searchDraftKeyword.value = searchKeyword.value
|
||||
void loadGuideFloors()
|
||||
void loadFacilityResults()
|
||||
})
|
||||
|
||||
|
||||
120
src/repositories/ExplainRepository.ts
Normal file
120
src/repositories/ExplainRepository.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type {
|
||||
ExplainTrack,
|
||||
MuseumExhibit,
|
||||
MuseumHall,
|
||||
SearchIndexItem
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
mediaRepository,
|
||||
type MediaRepository
|
||||
} from '@/repositories/MediaRepository'
|
||||
import {
|
||||
museumContentRepository,
|
||||
type MuseumContentRepository
|
||||
} from '@/repositories/MuseumContentRepository'
|
||||
|
||||
export interface ExplainRepository {
|
||||
listExhibits(): Promise<MuseumExhibit[]>
|
||||
getExhibitById(id: string): Promise<MuseumExhibit | null>
|
||||
listHalls(): Promise<MuseumHall[]>
|
||||
getHallById(id: string): Promise<MuseumHall | null>
|
||||
listTracks(): Promise<ExplainTrack[]>
|
||||
getTrackByExhibitId(exhibitId: string): Promise<ExplainTrack | null>
|
||||
searchExplain(keyword?: string): Promise<SearchIndexItem[]>
|
||||
}
|
||||
|
||||
const trackTitleFor = (exhibit: MuseumExhibit) => `${exhibit.name}讲解`
|
||||
|
||||
export class DefaultExplainRepository implements ExplainRepository {
|
||||
constructor(
|
||||
private readonly content: MuseumContentRepository = museumContentRepository,
|
||||
private readonly media: MediaRepository = mediaRepository
|
||||
) {}
|
||||
|
||||
listExhibits() {
|
||||
return this.content.listExhibits()
|
||||
}
|
||||
|
||||
getExhibitById(id: string) {
|
||||
return this.content.getExhibitById(id)
|
||||
}
|
||||
|
||||
listHalls() {
|
||||
return this.content.listHalls()
|
||||
}
|
||||
|
||||
getHallById(id: string) {
|
||||
return this.content.getHallById(id)
|
||||
}
|
||||
|
||||
async listTracks() {
|
||||
const exhibits = await this.content.listExhibits()
|
||||
|
||||
return Promise.all(exhibits.map(async (exhibit): Promise<ExplainTrack> => {
|
||||
const media = await this.media.getMediaForExplainTrack(exhibit.id)
|
||||
return {
|
||||
id: `track-${exhibit.id}`,
|
||||
exhibitId: exhibit.id,
|
||||
hallId: exhibit.hallId,
|
||||
title: trackTitleFor(exhibit),
|
||||
summary: exhibit.description,
|
||||
mediaId: media?.id,
|
||||
coverImage: exhibit.image,
|
||||
poiId: exhibit.poiId,
|
||||
floorId: exhibit.floorId,
|
||||
available: media?.available === true
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
async getTrackByExhibitId(exhibitId: string) {
|
||||
const tracks = await this.listTracks()
|
||||
return tracks.find((track) => track.exhibitId === exhibitId) || null
|
||||
}
|
||||
|
||||
async searchExplain(keyword = '') {
|
||||
const normalizedKeyword = keyword.trim().toLowerCase()
|
||||
const [exhibits, halls] = await Promise.all([
|
||||
this.content.listExhibits(),
|
||||
this.content.listHalls()
|
||||
])
|
||||
|
||||
const exhibitItems = exhibits
|
||||
.filter((exhibit) => !normalizedKeyword || [
|
||||
exhibit.name,
|
||||
exhibit.hallName,
|
||||
exhibit.floorLabel,
|
||||
...(exhibit.tags || [])
|
||||
].filter(Boolean).join(' ').toLowerCase().includes(normalizedKeyword))
|
||||
.map<SearchIndexItem>((exhibit) => ({
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
desc: `${exhibit.hallName || '展项'} · ${exhibit.floorLabel || ''}`.trim(),
|
||||
type: 'exhibit',
|
||||
exhibitId: exhibit.id,
|
||||
hallId: exhibit.hallId,
|
||||
poiId: exhibit.poiId,
|
||||
floorId: exhibit.floorId
|
||||
}))
|
||||
|
||||
const hallItems = halls
|
||||
.filter((hall) => !normalizedKeyword || [
|
||||
hall.name,
|
||||
hall.floorLabel,
|
||||
hall.description
|
||||
].filter(Boolean).join(' ').toLowerCase().includes(normalizedKeyword))
|
||||
.map<SearchIndexItem>((hall) => ({
|
||||
id: hall.id,
|
||||
name: hall.name,
|
||||
desc: `${hall.floorLabel || ''} · ${hall.exhibitCount || 0}件展项`.trim(),
|
||||
type: 'hall',
|
||||
hallId: hall.id,
|
||||
poiId: hall.poiId,
|
||||
floorId: hall.floorId
|
||||
}))
|
||||
|
||||
return [...exhibitItems, ...hallItems]
|
||||
}
|
||||
}
|
||||
|
||||
export const explainRepository = new DefaultExplainRepository()
|
||||
64
src/repositories/GuideModelRepository.ts
Normal file
64
src/repositories/GuideModelRepository.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type {
|
||||
GuideModelFloorAsset,
|
||||
GuideModelRenderPackage,
|
||||
GuideModelSource,
|
||||
GuideRenderPoi
|
||||
} from '@/domain/guideModel'
|
||||
import {
|
||||
formatNavFloorLabel
|
||||
} from '@/data/adapters/navAssetsAdapter'
|
||||
import {
|
||||
defaultStaticNavAssetsProvider,
|
||||
type StaticNavAssetsProvider,
|
||||
type StaticNavPoiPayload
|
||||
} from '@/data/providers/staticNavAssetsProvider'
|
||||
|
||||
const toRenderPoi = (poi: StaticNavPoiPayload): GuideRenderPoi => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
floorId: poi.floorId,
|
||||
primaryCategory: poi.primaryCategory,
|
||||
primaryCategoryZh: poi.primaryCategoryZh,
|
||||
iconType: poi.iconType || poi.primaryCategory,
|
||||
positionGltf: poi.positionGltf
|
||||
})
|
||||
|
||||
export interface GuideModelRepository extends GuideModelSource {}
|
||||
|
||||
export class StaticGuideModelRepository implements GuideModelRepository {
|
||||
constructor(private readonly provider: StaticNavAssetsProvider = defaultStaticNavAssetsProvider) {}
|
||||
|
||||
async loadPackage(): Promise<GuideModelRenderPackage> {
|
||||
const [manifest, floorIndex] = await Promise.all([
|
||||
this.provider.loadManifest(),
|
||||
this.provider.loadFloorIndex()
|
||||
])
|
||||
|
||||
const floors: GuideModelFloorAsset[] = [...floorIndex.floors]
|
||||
.sort((a, b) => a.order - b.order)
|
||||
.map((floor) => ({
|
||||
floorId: floor.floorId,
|
||||
label: formatNavFloorLabel(floor.floorId),
|
||||
order: floor.order,
|
||||
modelUrl: this.provider.assetUrl(floor.modelAsset)
|
||||
}))
|
||||
|
||||
return {
|
||||
overviewModelUrl: this.provider.assetUrl(manifest.assets.overviewModel.asset),
|
||||
floors
|
||||
}
|
||||
}
|
||||
|
||||
async loadFloorPois(floorId: string) {
|
||||
const floorIndex = await this.provider.loadFloorIndex()
|
||||
const floor = floorIndex.floors.find((item) => item.floorId === floorId)
|
||||
if (!floor) return []
|
||||
|
||||
const pois = await this.provider.loadFloorPois(floor.poiDataAsset)
|
||||
return pois
|
||||
.map(toRenderPoi)
|
||||
.filter((poi) => Array.isArray(poi.positionGltf) && poi.positionGltf.length === 3)
|
||||
}
|
||||
}
|
||||
|
||||
export const guideModelRepository = new StaticGuideModelRepository()
|
||||
122
src/repositories/GuideRepository.ts
Normal file
122
src/repositories/GuideRepository.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type {
|
||||
GuideLocationPreview,
|
||||
MuseumFloor,
|
||||
MuseumPoi
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
NAV_ROUTE_READINESS
|
||||
} from '@/domain/guideReadiness'
|
||||
import {
|
||||
formatNavFloorLabel,
|
||||
navFloorIdFromLabel,
|
||||
toMuseumPoi
|
||||
} from '@/data/adapters/navAssetsAdapter'
|
||||
import {
|
||||
defaultStaticNavAssetsProvider,
|
||||
type StaticNavAssetsProvider
|
||||
} from '@/data/providers/staticNavAssetsProvider'
|
||||
|
||||
const searchableCategories = new Set([
|
||||
'touring_poi',
|
||||
'basic_service_facility',
|
||||
'transport_circulation',
|
||||
'accessibility_special_service',
|
||||
'operation_experience'
|
||||
])
|
||||
|
||||
const toSearchText = (poi: MuseumPoi) => [
|
||||
poi.name,
|
||||
poi.floorId,
|
||||
poi.floorLabel,
|
||||
poi.primaryCategory.label,
|
||||
poi.primaryCategory.iconType,
|
||||
...poi.categories.flatMap((category) => [
|
||||
category.label,
|
||||
category.id,
|
||||
category.iconType
|
||||
])
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
|
||||
const toLocationPreview = (poi: MuseumPoi): GuideLocationPreview => ({
|
||||
poiId: poi.id,
|
||||
name: poi.name,
|
||||
floorId: poi.floorId,
|
||||
floorLabel: poi.floorLabel,
|
||||
primaryCategoryZh: poi.primaryCategory.label,
|
||||
positionGltf: poi.positionGltf
|
||||
})
|
||||
|
||||
export interface GuideRepository {
|
||||
getAssetBaseUrl(): string
|
||||
getFloors(): Promise<MuseumFloor[]>
|
||||
normalizeFloorId(labelOrId: string): string
|
||||
listPois(): Promise<MuseumPoi[]>
|
||||
getPoiById(id: string): Promise<MuseumPoi | null>
|
||||
searchPois(keyword?: string): Promise<MuseumPoi[]>
|
||||
getLocationPreview(poiId: string): Promise<GuideLocationPreview | null>
|
||||
getRouteReadiness(): typeof NAV_ROUTE_READINESS
|
||||
}
|
||||
|
||||
export class StaticGuideRepository implements GuideRepository {
|
||||
private poiCache: MuseumPoi[] | null = null
|
||||
|
||||
constructor(private readonly provider: StaticNavAssetsProvider = defaultStaticNavAssetsProvider) {}
|
||||
|
||||
getAssetBaseUrl() {
|
||||
return this.provider.baseUrl
|
||||
}
|
||||
|
||||
async getFloors() {
|
||||
const floorIndex = await this.provider.loadFloorIndex()
|
||||
return [...floorIndex.floors]
|
||||
.sort((a, b) => b.order - a.order)
|
||||
.map((floor) => ({
|
||||
id: floor.floorId,
|
||||
label: formatNavFloorLabel(floor.floorId),
|
||||
order: floor.order
|
||||
}))
|
||||
}
|
||||
|
||||
normalizeFloorId(labelOrId: string) {
|
||||
return navFloorIdFromLabel(labelOrId)
|
||||
}
|
||||
|
||||
async listPois() {
|
||||
if (this.poiCache) return this.poiCache
|
||||
|
||||
const pois = await this.provider.loadPoiIndex()
|
||||
this.poiCache = pois
|
||||
.filter((poi) => searchableCategories.has(poi.primaryCategory))
|
||||
.map(toMuseumPoi)
|
||||
|
||||
return this.poiCache
|
||||
}
|
||||
|
||||
async getPoiById(id: string) {
|
||||
const pois = await this.listPois()
|
||||
return pois.find((poi) => poi.id === id) || null
|
||||
}
|
||||
|
||||
async searchPois(keyword = '') {
|
||||
const pois = await this.listPois()
|
||||
const normalizedKeyword = keyword.trim().toLowerCase()
|
||||
|
||||
if (!normalizedKeyword) return pois
|
||||
|
||||
return pois.filter((poi) => toSearchText(poi).includes(normalizedKeyword))
|
||||
}
|
||||
|
||||
async getLocationPreview(poiId: string) {
|
||||
const poi = await this.getPoiById(poiId)
|
||||
return poi ? toLocationPreview(poi) : null
|
||||
}
|
||||
|
||||
getRouteReadiness() {
|
||||
return NAV_ROUTE_READINESS
|
||||
}
|
||||
}
|
||||
|
||||
export const guideRepository = new StaticGuideRepository()
|
||||
27
src/repositories/MediaRepository.ts
Normal file
27
src/repositories/MediaRepository.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type {
|
||||
MediaAsset
|
||||
} from '@/domain/museum'
|
||||
|
||||
export interface MediaRepository {
|
||||
getMediaById(id: string): Promise<MediaAsset | null>
|
||||
getMediaForExplainTrack(trackId: string): Promise<MediaAsset | null>
|
||||
}
|
||||
|
||||
const unavailableAudio = (id: string): MediaAsset => ({
|
||||
id,
|
||||
type: 'audio',
|
||||
available: false,
|
||||
unavailableReason: '正式讲解音频尚未接入媒体仓储'
|
||||
})
|
||||
|
||||
export class DefaultMediaRepository implements MediaRepository {
|
||||
async getMediaById(id: string) {
|
||||
return unavailableAudio(id)
|
||||
}
|
||||
|
||||
async getMediaForExplainTrack(trackId: string) {
|
||||
return unavailableAudio(`media-${trackId}`)
|
||||
}
|
||||
}
|
||||
|
||||
export const mediaRepository = new DefaultMediaRepository()
|
||||
124
src/repositories/MuseumContentRepository.ts
Normal file
124
src/repositories/MuseumContentRepository.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type {
|
||||
MuseumExhibit,
|
||||
MuseumHall,
|
||||
MuseumPoi,
|
||||
SearchIndexItem
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
guideRepository,
|
||||
type GuideRepository
|
||||
} from '@/repositories/GuideRepository'
|
||||
import {
|
||||
staticMuseumContentProvider,
|
||||
type MuseumContentProvider
|
||||
} from '@/data/providers/staticMuseumContentProvider'
|
||||
|
||||
export interface MuseumContentRepository {
|
||||
listFacilities(): Promise<MuseumPoi[]>
|
||||
getFacilityById(id: string): Promise<MuseumPoi | null>
|
||||
searchFacilities(keyword?: string): Promise<MuseumPoi[]>
|
||||
listExhibits(): Promise<MuseumExhibit[]>
|
||||
getExhibitById(id: string): Promise<MuseumExhibit | null>
|
||||
listHalls(): Promise<MuseumHall[]>
|
||||
getHallById(id: string): Promise<MuseumHall | null>
|
||||
searchContent(keyword?: string): Promise<SearchIndexItem[]>
|
||||
}
|
||||
|
||||
const normalize = (value: string) => value.trim().toLowerCase()
|
||||
const includesKeyword = (values: Array<string | undefined>, keyword: string) => (
|
||||
values.filter(Boolean).join(' ').toLowerCase().includes(keyword)
|
||||
)
|
||||
|
||||
export class DefaultMuseumContentRepository implements MuseumContentRepository {
|
||||
constructor(
|
||||
private readonly contentProvider: MuseumContentProvider = staticMuseumContentProvider,
|
||||
private readonly guide: GuideRepository = guideRepository
|
||||
) {}
|
||||
|
||||
listFacilities() {
|
||||
return this.guide.listPois()
|
||||
}
|
||||
|
||||
getFacilityById(id: string) {
|
||||
return this.guide.getPoiById(id)
|
||||
}
|
||||
|
||||
searchFacilities(keyword = '') {
|
||||
return this.guide.searchPois(keyword)
|
||||
}
|
||||
|
||||
listExhibits() {
|
||||
return this.contentProvider.listExhibits()
|
||||
}
|
||||
|
||||
async getExhibitById(id: string) {
|
||||
const exhibits = await this.contentProvider.listExhibits()
|
||||
return exhibits.find((exhibit) => exhibit.id === id) || null
|
||||
}
|
||||
|
||||
listHalls() {
|
||||
return this.contentProvider.listHalls()
|
||||
}
|
||||
|
||||
async getHallById(id: string) {
|
||||
const halls = await this.contentProvider.listHalls()
|
||||
return halls.find((hall) => hall.id === id) || null
|
||||
}
|
||||
|
||||
async searchContent(keyword = '') {
|
||||
const normalizedKeyword = normalize(keyword)
|
||||
const facilities = await this.searchFacilities(keyword)
|
||||
const exhibits = await this.listExhibits()
|
||||
const halls = await this.listHalls()
|
||||
|
||||
const exhibitItems = exhibits
|
||||
.filter((exhibit) => !normalizedKeyword || includesKeyword([
|
||||
exhibit.name,
|
||||
exhibit.hallName,
|
||||
exhibit.floorLabel,
|
||||
...(exhibit.tags || [])
|
||||
], normalizedKeyword))
|
||||
.map<SearchIndexItem>((exhibit) => ({
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
desc: `${exhibit.hallName || '展项'} · ${exhibit.floorLabel || ''}`.trim(),
|
||||
type: 'exhibit',
|
||||
exhibitId: exhibit.id,
|
||||
poiId: exhibit.poiId,
|
||||
floorId: exhibit.floorId
|
||||
}))
|
||||
|
||||
const hallItems = halls
|
||||
.filter((hall) => !normalizedKeyword || includesKeyword([
|
||||
hall.name,
|
||||
hall.floorLabel,
|
||||
hall.description
|
||||
], normalizedKeyword))
|
||||
.map<SearchIndexItem>((hall) => ({
|
||||
id: hall.id,
|
||||
name: hall.name,
|
||||
desc: `${hall.floorLabel || ''} · ${hall.exhibitCount || 0}件展项`.trim(),
|
||||
type: 'hall',
|
||||
hallId: hall.id,
|
||||
poiId: hall.poiId,
|
||||
floorId: hall.floorId
|
||||
}))
|
||||
|
||||
const facilityItems = facilities.map<SearchIndexItem>((poi) => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
desc: `${poi.floorLabel} · ${poi.primaryCategory.label}`,
|
||||
type: 'facility',
|
||||
poiId: poi.id,
|
||||
floorId: poi.floorId
|
||||
}))
|
||||
|
||||
return [
|
||||
...exhibitItems,
|
||||
...hallItems,
|
||||
...facilityItems
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const museumContentRepository = new DefaultMuseumContentRepository()
|
||||
@@ -1,164 +0,0 @@
|
||||
export const NAV_ASSET_BASE_URL = '/static/nav-assets/app_nav_assets_v2_clean_20260609_075339'
|
||||
|
||||
export const NAV_ROUTE_UNAVAILABLE_MESSAGE = '正式路线数据尚未接入,可先查看馆内三维位置'
|
||||
|
||||
export interface NavFloorOption {
|
||||
id: string
|
||||
label: string
|
||||
order: number
|
||||
}
|
||||
|
||||
export interface CleanNavPoiCategory {
|
||||
topCategory: string
|
||||
topCategoryZh: string
|
||||
subcategory: string
|
||||
iconType: string
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export interface CleanNavPoi {
|
||||
id: string
|
||||
name: string
|
||||
floorId: string
|
||||
primaryCategory: string
|
||||
primaryCategoryZh: string
|
||||
categories?: CleanNavPoiCategory[]
|
||||
iconType?: string
|
||||
positionGltf?: [number, number, number]
|
||||
navigationReadiness?: string
|
||||
sourceConfidence?: string
|
||||
}
|
||||
|
||||
interface CleanPoiIndex {
|
||||
status: string
|
||||
pois: CleanNavPoi[]
|
||||
}
|
||||
|
||||
export const NAV_FLOOR_OPTIONS: NavFloorOption[] = [
|
||||
{ id: 'L5', label: '5F', order: 5 },
|
||||
{ id: 'L4', label: '4F', order: 4 },
|
||||
{ id: 'L3', label: '3F', order: 3 },
|
||||
{ id: 'L2', label: '2F', order: 2 },
|
||||
{ id: 'L1.5', label: '1.5F', order: 1.5 },
|
||||
{ id: 'L1', label: '1F', order: 1 },
|
||||
{ id: 'L-1', label: 'B1', order: -1 },
|
||||
{ id: 'L-2', label: 'B2', order: -2 }
|
||||
]
|
||||
|
||||
const searchableCategories = new Set([
|
||||
'touring_poi',
|
||||
'basic_service_facility',
|
||||
'transport_circulation',
|
||||
'accessibility_special_service',
|
||||
'operation_experience'
|
||||
])
|
||||
|
||||
const normalizeBaseUrl = (baseUrl: string) => baseUrl.replace(/\/+$/, '')
|
||||
const poiCache = new Map<string, CleanNavPoi[]>()
|
||||
const poiRequestCache = new Map<string, Promise<CleanNavPoi[]>>()
|
||||
|
||||
export const navAssetUrl = (relativePath: string, baseUrl = NAV_ASSET_BASE_URL) => (
|
||||
`${normalizeBaseUrl(baseUrl)}/${relativePath.replace(/^\/+/, '')}`
|
||||
)
|
||||
|
||||
export const formatNavFloorLabel = (floorId: string) => (
|
||||
NAV_FLOOR_OPTIONS.find((floor) => floor.id === floorId)?.label || floorId
|
||||
)
|
||||
|
||||
export const navFloorIdFromLabel = (labelOrId: string) => (
|
||||
NAV_FLOOR_OPTIONS.find((floor) => floor.label === labelOrId || floor.id === labelOrId)?.id || labelOrId
|
||||
)
|
||||
|
||||
export const isPoiAccessible = (poi: CleanNavPoi) => (
|
||||
poi.primaryCategory === 'accessibility_special_service'
|
||||
|| poi.categories?.some((category) => category.topCategory === 'accessibility_special_service') === true
|
||||
)
|
||||
|
||||
const parseJsonPayload = <T>(payload: unknown): T => {
|
||||
if (typeof payload === 'string') {
|
||||
return JSON.parse(payload) as T
|
||||
}
|
||||
|
||||
return payload as T
|
||||
}
|
||||
|
||||
const requestJson = <T>(url: string): Promise<T> => new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
url,
|
||||
method: 'GET',
|
||||
success: (response) => {
|
||||
const statusCode = Number(response.statusCode || 0)
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
reject(new Error(`导览资源读取失败: ${statusCode} ${url}`))
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
resolve(parseJsonPayload<T>(response.data))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
},
|
||||
fail: reject
|
||||
})
|
||||
})
|
||||
|
||||
export const loadCleanNavPois = async (baseUrl = NAV_ASSET_BASE_URL) => {
|
||||
const cacheKey = normalizeBaseUrl(baseUrl)
|
||||
const cachedPois = poiCache.get(cacheKey)
|
||||
if (cachedPois) return cachedPois
|
||||
|
||||
const pendingPois = poiRequestCache.get(cacheKey)
|
||||
if (pendingPois) return pendingPois
|
||||
|
||||
const request = requestJson<CleanPoiIndex>(navAssetUrl('data/poi_all.json', cacheKey))
|
||||
.then((data) => {
|
||||
if (data.status !== 'pass') {
|
||||
throw new Error('导览 POI 数据状态不是 pass')
|
||||
}
|
||||
|
||||
const pois = data.pois.filter((poi) => searchableCategories.has(poi.primaryCategory))
|
||||
poiCache.set(cacheKey, pois)
|
||||
return pois
|
||||
})
|
||||
.finally(() => {
|
||||
poiRequestCache.delete(cacheKey)
|
||||
})
|
||||
|
||||
poiRequestCache.set(cacheKey, request)
|
||||
return request
|
||||
}
|
||||
|
||||
export const loadCleanNavPoiById = async (id: string, baseUrl = NAV_ASSET_BASE_URL) => {
|
||||
const pois = await loadCleanNavPois(baseUrl)
|
||||
return pois.find((poi) => poi.id === id) || null
|
||||
}
|
||||
|
||||
export const searchCleanNavPois = async (keyword = '', baseUrl = NAV_ASSET_BASE_URL) => {
|
||||
const pois = await loadCleanNavPois(baseUrl)
|
||||
const normalizedKeyword = keyword.trim().toLowerCase()
|
||||
|
||||
const matchedPois = normalizedKeyword
|
||||
? pois.filter((poi) => {
|
||||
const searchableText = [
|
||||
poi.name,
|
||||
poi.floorId,
|
||||
formatNavFloorLabel(poi.floorId),
|
||||
poi.primaryCategoryZh,
|
||||
poi.iconType,
|
||||
...(poi.categories || []).flatMap((category) => [
|
||||
category.topCategoryZh,
|
||||
category.subcategory,
|
||||
category.iconType
|
||||
])
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
|
||||
return searchableText.includes(normalizedKeyword)
|
||||
})
|
||||
: pois
|
||||
|
||||
return matchedPois
|
||||
}
|
||||
75
src/usecases/explainUseCase.ts
Normal file
75
src/usecases/explainUseCase.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import type {
|
||||
ExplainTrack,
|
||||
MediaAsset,
|
||||
MuseumExhibit,
|
||||
MuseumHall,
|
||||
SearchIndexItem
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
explainRepository,
|
||||
type ExplainRepository
|
||||
} from '@/repositories/ExplainRepository'
|
||||
import {
|
||||
mediaRepository,
|
||||
type MediaRepository
|
||||
} from '@/repositories/MediaRepository'
|
||||
|
||||
export interface ExplainAudioSelection {
|
||||
exhibit: MuseumExhibit
|
||||
track: ExplainTrack | null
|
||||
media: MediaAsset | null
|
||||
playable: boolean
|
||||
unavailableMessage?: string
|
||||
}
|
||||
|
||||
export class ExplainUseCase {
|
||||
constructor(
|
||||
private readonly explain: ExplainRepository = explainRepository,
|
||||
private readonly media: MediaRepository = mediaRepository
|
||||
) {}
|
||||
|
||||
listExhibits() {
|
||||
return this.explain.listExhibits()
|
||||
}
|
||||
|
||||
getExhibitById(id: string) {
|
||||
return this.explain.getExhibitById(id)
|
||||
}
|
||||
|
||||
listHalls(): Promise<MuseumHall[]> {
|
||||
return this.explain.listHalls()
|
||||
}
|
||||
|
||||
getHallById(id: string) {
|
||||
return this.explain.getHallById(id)
|
||||
}
|
||||
|
||||
async listExhibitsByHallId(hallId: string) {
|
||||
const exhibits = await this.explain.listExhibits()
|
||||
return exhibits.filter((exhibit) => exhibit.hallId === hallId)
|
||||
}
|
||||
|
||||
searchExplain(keyword?: string): Promise<SearchIndexItem[]> {
|
||||
return this.explain.searchExplain(keyword)
|
||||
}
|
||||
|
||||
async selectAudioForExhibit(exhibitId: string): Promise<ExplainAudioSelection | null> {
|
||||
const exhibit = await this.explain.getExhibitById(exhibitId)
|
||||
if (!exhibit) return null
|
||||
|
||||
const track = await this.explain.getTrackByExhibitId(exhibitId)
|
||||
const media = track?.mediaId
|
||||
? await this.media.getMediaById(track.mediaId)
|
||||
: null
|
||||
|
||||
return {
|
||||
exhibit,
|
||||
track,
|
||||
media,
|
||||
playable: media?.available === true && Boolean(media.url),
|
||||
unavailableMessage: media?.unavailableReason || '该展项暂无讲解音频'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const explainUseCase = new ExplainUseCase()
|
||||
124
src/usecases/guideUseCase.ts
Normal file
124
src/usecases/guideUseCase.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type {
|
||||
GuideLocationPreviewPlan,
|
||||
GuidePreviewStep,
|
||||
GuideStartLocation,
|
||||
MuseumPoi
|
||||
} from '@/domain/museum'
|
||||
import {
|
||||
guideRepository,
|
||||
type GuideRepository
|
||||
} from '@/repositories/GuideRepository'
|
||||
import {
|
||||
guideModelRepository,
|
||||
type GuideModelRepository
|
||||
} from '@/repositories/GuideModelRepository'
|
||||
|
||||
const startSourceLabels: Record<string, string> = {
|
||||
'facility-detail': '设施详情选择',
|
||||
manual: '手动选择'
|
||||
}
|
||||
|
||||
const formatStartSourceLabel = (source?: string) => (
|
||||
source ? startSourceLabels[source] || source : ''
|
||||
)
|
||||
|
||||
const formatStartStepText = (startLocation: GuideStartLocation) => {
|
||||
const sourceLabel = formatStartSourceLabel(startLocation.source)
|
||||
const floorSuffix = startLocation.floor && !startLocation.label.includes(startLocation.floor)
|
||||
? ` · ${startLocation.floor}`
|
||||
: ''
|
||||
const sourceSuffix = sourceLabel ? `(${sourceLabel})` : ''
|
||||
|
||||
return `起点:${startLocation.label}${floorSuffix}${sourceSuffix}`
|
||||
}
|
||||
|
||||
const createPreviewSteps = (
|
||||
poi: MuseumPoi | null,
|
||||
startLocation: GuideStartLocation | null,
|
||||
routeUnavailableMessage: string
|
||||
): GuidePreviewStep[] => {
|
||||
if (startLocation) {
|
||||
return [
|
||||
{
|
||||
id: 'start',
|
||||
text: formatStartStepText(startLocation)
|
||||
},
|
||||
{
|
||||
id: 'target',
|
||||
text: poi
|
||||
? `目标:${poi.floorLabel} · ${poi.primaryCategory.label} · 位置预览`
|
||||
: '目标来自 clean 导览数据,暂仅支持位置预览'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'floor',
|
||||
text: poi ? `目标位于 ${poi.floorLabel} · ${poi.primaryCategory.label}` : '目标来自 clean 导览数据'
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
text: routeUnavailableMessage
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export class GuideUseCase {
|
||||
constructor(
|
||||
private readonly guide: GuideRepository = guideRepository,
|
||||
private readonly guideModel: GuideModelRepository = guideModelRepository
|
||||
) {}
|
||||
|
||||
getRouteReadiness() {
|
||||
return this.guide.getRouteReadiness()
|
||||
}
|
||||
|
||||
getFloors() {
|
||||
return this.guide.getFloors()
|
||||
}
|
||||
|
||||
getAssetBaseUrl() {
|
||||
return this.guide.getAssetBaseUrl()
|
||||
}
|
||||
|
||||
getModelSource() {
|
||||
return this.guideModel
|
||||
}
|
||||
|
||||
normalizeFloorId(labelOrId: string) {
|
||||
return this.guide.normalizeFloorId(labelOrId)
|
||||
}
|
||||
|
||||
searchPois(keyword?: string) {
|
||||
return this.guide.searchPois(keyword)
|
||||
}
|
||||
|
||||
getPoiById(id: string) {
|
||||
return this.guide.getPoiById(id)
|
||||
}
|
||||
|
||||
async createLocationPreviewPlan(
|
||||
facilityId: string,
|
||||
target: string,
|
||||
startLocation: GuideStartLocation | null = null
|
||||
): Promise<GuideLocationPreviewPlan> {
|
||||
const poi = facilityId ? await this.guide.getPoiById(facilityId) : null
|
||||
const routeReadiness = this.guide.getRouteReadiness()
|
||||
const targetLocation = poi ? await this.guide.getLocationPreview(poi.id) : null
|
||||
|
||||
return {
|
||||
id: `route-${facilityId || 'custom'}`,
|
||||
facilityId,
|
||||
target: target || poi?.name || '目标地点',
|
||||
summary: poi
|
||||
? `${poi.floorLabel} · ${poi.primaryCategory.label} · 位置预览`
|
||||
: '位置预览 · 尚未接入正式路线图',
|
||||
steps: createPreviewSteps(poi, startLocation, routeReadiness.message),
|
||||
targetLocation,
|
||||
startLocation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const guideUseCase = new GuideUseCase()
|
||||
@@ -1,98 +0,0 @@
|
||||
/**
|
||||
* Legacy demo data loader.
|
||||
*
|
||||
* 当前导览模块不要再从 src/assets/data 取数;室内导览、搜索、位置预览
|
||||
* 统一使用 src/services/navAssets.ts 读取 clean nav assets。
|
||||
* 这里保留给尚未重构的历史演示页面,避免删除造成不可控回归。
|
||||
*/
|
||||
|
||||
// 加载楼层数据
|
||||
export const loadFloors = async () => {
|
||||
try {
|
||||
const data = await import('@/assets/data/floors.json')
|
||||
return data.default || data
|
||||
} catch (error) {
|
||||
console.error('加载楼层数据失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 加载展厅数据
|
||||
export const loadHalls = async () => {
|
||||
try {
|
||||
const data = await import('@/assets/data/halls.json')
|
||||
return data.default || data
|
||||
} catch (error) {
|
||||
console.error('加载展厅数据失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 加载展品数据
|
||||
export const loadExhibits = async () => {
|
||||
try {
|
||||
const data = await import('@/assets/data/exhibits.json')
|
||||
return data.default || data
|
||||
} catch (error) {
|
||||
console.error('加载展品数据失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 加载设施数据
|
||||
export const loadFacilities = async () => {
|
||||
try {
|
||||
const data = await import('@/assets/data/facilities.json')
|
||||
return data.default || data
|
||||
} catch (error) {
|
||||
console.error('加载设施数据失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 加载路线数据
|
||||
export const loadRoutes = async () => {
|
||||
try {
|
||||
const data = await import('@/assets/data/routes.json')
|
||||
return data.default || data
|
||||
} catch (error) {
|
||||
console.error('加载路线数据失败:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 ID 查找展品
|
||||
export const findExhibitById = async (id: string) => {
|
||||
const exhibits = await loadExhibits()
|
||||
return exhibits.find((item: any) => item.id === id)
|
||||
}
|
||||
|
||||
// 根据 ID 查找展厅
|
||||
export const findHallById = async (id: string) => {
|
||||
const halls = await loadHalls()
|
||||
return halls.find((item: any) => item.id === id)
|
||||
}
|
||||
|
||||
// 根据 ID 查找设施
|
||||
export const findFacilityById = async (id: string) => {
|
||||
const facilities = await loadFacilities()
|
||||
return facilities.find((item: any) => item.id === id)
|
||||
}
|
||||
|
||||
// 根据 ID 查找路线
|
||||
export const findRouteById = async (id: string) => {
|
||||
const routes = await loadRoutes()
|
||||
return routes.find((item: any) => item.id === id)
|
||||
}
|
||||
|
||||
// 根据楼层筛选展厅
|
||||
export const filterHallsByFloor = async (floorId: string) => {
|
||||
const halls = await loadHalls()
|
||||
return halls.filter((item: any) => item.floor === floorId)
|
||||
}
|
||||
|
||||
// 根据展厅筛选展品
|
||||
export const filterExhibitsByHall = async (hallId: string) => {
|
||||
const exhibits = await loadExhibits()
|
||||
return exhibits.filter((item: any) => item.hallId === hallId)
|
||||
}
|
||||
90
src/view-models/explainViewModels.ts
Normal file
90
src/view-models/explainViewModels.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import type {
|
||||
MuseumExhibit,
|
||||
MuseumHall,
|
||||
SearchIndexItem
|
||||
} from '@/domain/museum'
|
||||
|
||||
export interface ExplainExhibitViewModel {
|
||||
id: string
|
||||
name: string
|
||||
hall?: string
|
||||
floor?: string
|
||||
image?: string
|
||||
hasAudio: boolean
|
||||
audioUnavailableReason?: string
|
||||
poiId?: string
|
||||
}
|
||||
|
||||
export interface ExplainDetailViewModel {
|
||||
id: string
|
||||
name: string
|
||||
artist?: string
|
||||
year?: string
|
||||
material?: string
|
||||
size?: string
|
||||
hall?: string
|
||||
image: string
|
||||
description: string
|
||||
hasAudio: boolean
|
||||
audioUnavailableReason?: string
|
||||
poiId?: string
|
||||
}
|
||||
|
||||
export interface HallDetailViewModel {
|
||||
id: string
|
||||
name: string
|
||||
floorLabel: string
|
||||
description: string
|
||||
image: string
|
||||
exhibitCount: number
|
||||
area?: string
|
||||
poiId?: string
|
||||
}
|
||||
|
||||
export const toExplainExhibitViewModel = (exhibit: MuseumExhibit): ExplainExhibitViewModel => ({
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
hall: exhibit.hallName && exhibit.floorLabel
|
||||
? `${exhibit.hallName} ${exhibit.floorLabel}`
|
||||
: exhibit.hallName || exhibit.floorLabel,
|
||||
floor: exhibit.floorLabel,
|
||||
image: exhibit.image,
|
||||
hasAudio: false,
|
||||
audioUnavailableReason: '正式讲解音频尚未接入媒体仓储',
|
||||
poiId: exhibit.poiId
|
||||
})
|
||||
|
||||
export const toExplainDetailViewModel = (exhibit: MuseumExhibit): ExplainDetailViewModel => ({
|
||||
id: exhibit.id,
|
||||
name: exhibit.name,
|
||||
artist: exhibit.artist,
|
||||
year: exhibit.year,
|
||||
material: exhibit.material,
|
||||
size: exhibit.size,
|
||||
hall: exhibit.hallName && exhibit.floorLabel
|
||||
? `${exhibit.hallName} ${exhibit.floorLabel}`
|
||||
: exhibit.hallName || exhibit.floorLabel,
|
||||
image: exhibit.image || '/static/exhibit-placeholder.jpg',
|
||||
description: exhibit.description || '该展项介绍待正式内容库补充。',
|
||||
hasAudio: false,
|
||||
audioUnavailableReason: '正式讲解音频尚未接入媒体仓储',
|
||||
poiId: exhibit.poiId
|
||||
})
|
||||
|
||||
export const toHallDetailViewModel = (hall: MuseumHall): HallDetailViewModel => ({
|
||||
id: hall.id,
|
||||
name: hall.name,
|
||||
floorLabel: hall.floorLabel || '楼层待补充',
|
||||
description: hall.description || '该展厅介绍待正式内容库补充。',
|
||||
image: hall.image || '/static/hall-placeholder.jpg',
|
||||
exhibitCount: hall.exhibitCount || 0,
|
||||
area: hall.area,
|
||||
poiId: hall.poiId
|
||||
})
|
||||
|
||||
export const toExplainSearchResultViewModel = (item: SearchIndexItem) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
desc: item.desc,
|
||||
type: item.type
|
||||
})
|
||||
70
src/view-models/guideViewModels.ts
Normal file
70
src/view-models/guideViewModels.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import type {
|
||||
GuideLocationPreview,
|
||||
GuideLocationPreviewPlan,
|
||||
MuseumPoi
|
||||
} from '@/domain/museum'
|
||||
|
||||
export interface FacilityResultViewModel {
|
||||
id: string
|
||||
name: string
|
||||
floor: string
|
||||
meta: string
|
||||
accessible: boolean
|
||||
action: '查看'
|
||||
}
|
||||
|
||||
export interface FacilityDetailViewModel {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
location: string
|
||||
traffic: string
|
||||
tags: string[]
|
||||
}
|
||||
|
||||
export interface TargetPoiFocusRequestViewModel extends GuideLocationPreview {
|
||||
requestId: number | string
|
||||
}
|
||||
|
||||
export const toFacilityResultViewModel = (poi: MuseumPoi): FacilityResultViewModel => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
floor: poi.floorLabel,
|
||||
meta: `${poi.floorLabel}|${poi.primaryCategory.label}|${poi.navigationReadiness || '展示点位'}`,
|
||||
accessible: poi.accessible,
|
||||
action: '查看'
|
||||
})
|
||||
|
||||
export const toFacilityDetailViewModel = (
|
||||
poi: MuseumPoi,
|
||||
routeUnavailableMessage: string
|
||||
): FacilityDetailViewModel => ({
|
||||
id: poi.id,
|
||||
name: poi.name,
|
||||
status: '可预览',
|
||||
location: `${poi.floorLabel} · ${poi.primaryCategory.label}`,
|
||||
traffic: routeUnavailableMessage,
|
||||
tags: [
|
||||
poi.floorLabel,
|
||||
poi.primaryCategory.label,
|
||||
poi.accessible ? '无障碍相关' : '展示点位'
|
||||
]
|
||||
})
|
||||
|
||||
export const toTargetFocusRequestViewModel = (
|
||||
targetLocation: GuideLocationPreview,
|
||||
requestId: number | string
|
||||
): TargetPoiFocusRequestViewModel => ({
|
||||
...targetLocation,
|
||||
requestId
|
||||
})
|
||||
|
||||
export const initialLocationPreviewPlan = (): GuideLocationPreviewPlan => ({
|
||||
id: 'route-custom',
|
||||
facilityId: '',
|
||||
target: '目标地点',
|
||||
summary: '位置预览 · 尚未接入正式路线图',
|
||||
steps: [],
|
||||
targetLocation: null,
|
||||
startLocation: null
|
||||
})
|
||||
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,7 @@
|
||||
## 入口
|
||||
|
||||
- app_nav_manifest.json
|
||||
- building_overview.glb
|
||||
- building_overview.glb(无 L 楼层前缀的外墙/建筑外围/装饰对象)
|
||||
- data/poi_all.json
|
||||
- data/poi_by_floor/<floorId>.json
|
||||
- data/floor_index.json
|
||||
@@ -13,4 +13,5 @@
|
||||
## 说明
|
||||
|
||||
旧版 17 点 POI 与旧 poi_by_floor 目录没有复制到本包,避免小程序误读。
|
||||
models_by_floor/<floorId>.glb 只包含对象名以对应 L 楼层前缀开头的模型;无 L 前缀对象只进入 building_overview.glb。
|
||||
POI 坐标前端推荐使用 positionGltf。
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-nav-assets-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.467Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.719Z",
|
||||
"projectRoot": "E:/MyWork/深圳国际艺术馆/lender-museum",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"sourcePackage": "E:\\MyWork\\深圳国际艺术馆\\lender-museum\\evidence\\runs\\codex_nav_20260607_reloaded_223947\\app_nav_assets_20260609_072638",
|
||||
"outputRoot": "E:\\MyWork\\深圳国际艺术馆\\lender-museum\\evidence\\runs\\codex_nav_20260607_reloaded_223947\\app_nav_assets_v2_clean_20260609_075339",
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"sourcePackage": "E:\\MyWork\\深圳国际艺术馆\\lender-museum\\evidence\\runs\\codex_nav_20260607_reloaded_223947\\app_nav_assets_20260611_093410",
|
||||
"outputRoot": "E:\\MyWork\\深圳国际艺术馆\\lender-museum\\evidence\\runs\\codex_nav_20260607_reloaded_223947\\app_nav_assets_v2_clean_20260611_093623",
|
||||
"safety": {
|
||||
"opensSwitchesReloadsOrSavesBlend": false,
|
||||
"writesRouteGraph": false,
|
||||
@@ -16,6 +16,11 @@
|
||||
"writesWebModelCurrentSessionGlb": false,
|
||||
"blenderFree": true
|
||||
},
|
||||
"modelSeparationPolicy": {
|
||||
"floorModelRule": "models_by_floor/<floorId>.glb contains only mesh objects whose Blender object names start with that exact L floor prefix",
|
||||
"overviewModelRule": "building_overview.glb contains only no-floor-prefix exterior/envelope/decorative mesh objects",
|
||||
"noZHeuristicForModelExport": true
|
||||
},
|
||||
"frontendLoadingStrategy": {
|
||||
"initialView": [
|
||||
"load app_nav_manifest.json",
|
||||
@@ -38,82 +43,82 @@
|
||||
"assets": {
|
||||
"overviewModel": {
|
||||
"asset": "building_overview.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/building_overview.glb",
|
||||
"bytes": 9602412,
|
||||
"sha256": "6df745c8760db68ca5bdd95f91bd523fd29b33998bb7b269ed8453c4762e6050"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/building_overview.glb",
|
||||
"bytes": 3060532,
|
||||
"sha256": "64c5d0f7c82bf7fbdd1094e9c2adf5646c57f6e38666669e8daea77f1fbd0692"
|
||||
},
|
||||
"floorModels": [
|
||||
{
|
||||
"floorId": "L-2",
|
||||
"order": -2,
|
||||
"objectCount": 55,
|
||||
"objectCount": 65,
|
||||
"asset": "models_by_floor/L-2.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L-2.glb",
|
||||
"bytes": 2064656,
|
||||
"sha256": "72ba73e98b56e27875f8b8168e09f171cc3eb10b9208c8cc7970644d33a78198"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L-2.glb",
|
||||
"bytes": 4380824,
|
||||
"sha256": "3e7179f0b0aa6e4be9bdd9941814843762d20c496c1f0e84a59181f5925cc43a"
|
||||
},
|
||||
{
|
||||
"floorId": "L-1",
|
||||
"order": -1,
|
||||
"objectCount": 49,
|
||||
"objectCount": 50,
|
||||
"asset": "models_by_floor/L-1.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L-1.glb",
|
||||
"bytes": 1601948,
|
||||
"sha256": "617f958d17232938f26239f6fd3051894cae9d5a0e6fe9de526ecce03acf9db9"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L-1.glb",
|
||||
"bytes": 1604640,
|
||||
"sha256": "d0a9ee315722d44c6bd62e3275f7af582de23ffe5a8017eacf07b28b7a4f247a"
|
||||
},
|
||||
{
|
||||
"floorId": "L1",
|
||||
"order": 1,
|
||||
"objectCount": 90,
|
||||
"objectCount": 94,
|
||||
"asset": "models_by_floor/L1.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L1.glb",
|
||||
"bytes": 3992740,
|
||||
"sha256": "d1bf105c00bf7fa724d82dc8ac7c067c29dadc991dd550d404d4f3a75bab72cb"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L1.glb",
|
||||
"bytes": 8550188,
|
||||
"sha256": "315c8d325cda7de5f0650114e64b5ccd8279ae1b3155fd176caf313ee7dd1e50"
|
||||
},
|
||||
{
|
||||
"floorId": "L1.5",
|
||||
"order": 1.5,
|
||||
"objectCount": 26,
|
||||
"asset": "models_by_floor/L1.5.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L1.5.glb",
|
||||
"bytes": 1144212,
|
||||
"sha256": "6f372715c1837ec670cbfdfe2cee70bb1d11b2b6479ab95df81f0f863a00ad29"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L1.5.glb",
|
||||
"bytes": 1128032,
|
||||
"sha256": "43e82bf2c77fd961850644daf47e1222c1c1e58f850b696376c7e526e4cea7d0"
|
||||
},
|
||||
{
|
||||
"floorId": "L2",
|
||||
"order": 2,
|
||||
"objectCount": 50,
|
||||
"objectCount": 58,
|
||||
"asset": "models_by_floor/L2.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L2.glb",
|
||||
"bytes": 1595720,
|
||||
"sha256": "ca1581e89127275018d7402d1bbe0197b6aa28f0a77257bfe3bfffacf226b397"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L2.glb",
|
||||
"bytes": 2165388,
|
||||
"sha256": "481073fe17868f6fb3a563cd4f3d696da66a9e0c3db2c59485c96f1b41ba7e47"
|
||||
},
|
||||
{
|
||||
"floorId": "L3",
|
||||
"order": 3,
|
||||
"objectCount": 22,
|
||||
"objectCount": 18,
|
||||
"asset": "models_by_floor/L3.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L3.glb",
|
||||
"bytes": 3692804,
|
||||
"sha256": "f5099d6572dd4daf4f170678939839bb7b4f8a31fb5fe74eaaabc587cc31b21f"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L3.glb",
|
||||
"bytes": 821016,
|
||||
"sha256": "f9985fb5c7a7442be658a0359874e96a8f5155c7970bdf1933fa6f5f035c5d21"
|
||||
},
|
||||
{
|
||||
"floorId": "L4",
|
||||
"order": 4,
|
||||
"objectCount": 36,
|
||||
"asset": "models_by_floor/L4.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L4.glb",
|
||||
"bytes": 1073744,
|
||||
"sha256": "2f6c181ed840b4384878c9d3e138a865d3d355f7c17d65e8b48092875276181e"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L4.glb",
|
||||
"bytes": 1073656,
|
||||
"sha256": "9e86746cec793f3ae5dbfa57a3471cf231e9e2a686b9efa797df28182e6ffae4"
|
||||
},
|
||||
{
|
||||
"floorId": "L5",
|
||||
"order": 5,
|
||||
"objectCount": 15,
|
||||
"objectCount": 14,
|
||||
"asset": "models_by_floor/L5.glb",
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260609_075339/models_by_floor/L5.glb",
|
||||
"bytes": 911340,
|
||||
"sha256": "ca0aa271996842ae083f96aebeaffac46b4a0499ab901046d7aaeb60c282fcdb"
|
||||
"relativePath": "evidence/runs/codex_nav_20260607_reloaded_223947/app_nav_assets_v2_clean_20260611_093623/models_by_floor/L5.glb",
|
||||
"bytes": 751904,
|
||||
"sha256": "6dbe090d31a13edee95f860e032db214cdccf20786efc326a90e5e437d46933f"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -124,15 +129,15 @@
|
||||
"poiByFloor": {
|
||||
"L-2": {
|
||||
"asset": "data/poi_by_floor/L-2.json",
|
||||
"poiCount": 52
|
||||
"poiCount": 62
|
||||
},
|
||||
"L-1": {
|
||||
"asset": "data/poi_by_floor/L-1.json",
|
||||
"poiCount": 46
|
||||
"poiCount": 47
|
||||
},
|
||||
"L1": {
|
||||
"asset": "data/poi_by_floor/L1.json",
|
||||
"poiCount": 78
|
||||
"poiCount": 81
|
||||
},
|
||||
"L1.5": {
|
||||
"asset": "data/poi_by_floor/L1.5.json",
|
||||
@@ -140,11 +145,11 @@
|
||||
},
|
||||
"L2": {
|
||||
"asset": "data/poi_by_floor/L2.json",
|
||||
"poiCount": 47
|
||||
"poiCount": 55
|
||||
},
|
||||
"L3": {
|
||||
"asset": "data/poi_by_floor/L3.json",
|
||||
"poiCount": 16
|
||||
"poiCount": 15
|
||||
},
|
||||
"L4": {
|
||||
"asset": "data/poi_by_floor/L4.json",
|
||||
@@ -170,27 +175,27 @@
|
||||
"L4",
|
||||
"L5"
|
||||
],
|
||||
"overviewObjectCount": 45,
|
||||
"poiCount": 298,
|
||||
"sourceEnhancedPoiCount": 294,
|
||||
"supplementalPoiCount": 4,
|
||||
"overviewObjectCount": 5,
|
||||
"poiCount": 319,
|
||||
"sourceEnhancedPoiCount": 316,
|
||||
"supplementalPoiCount": 3,
|
||||
"poiCountByFloor": {
|
||||
"L-2": 52,
|
||||
"L-1": 46,
|
||||
"L1": 78,
|
||||
"L-2": 62,
|
||||
"L-1": 47,
|
||||
"L1": 81,
|
||||
"L1.5": 21,
|
||||
"L2": 47,
|
||||
"L3": 16,
|
||||
"L2": 55,
|
||||
"L3": 15,
|
||||
"L4": 27,
|
||||
"L5": 11
|
||||
},
|
||||
"poiCountByCategory": {
|
||||
"touring_poi": 17,
|
||||
"basic_service_facility": 47,
|
||||
"transport_circulation": 229,
|
||||
"touring_poi": 34,
|
||||
"basic_service_facility": 57,
|
||||
"transport_circulation": 232,
|
||||
"accessibility_special_service": 10,
|
||||
"safety_management": 0,
|
||||
"operation_experience": 8
|
||||
"operation_experience": 7
|
||||
},
|
||||
"connectorEndpointCount": 10
|
||||
},
|
||||
@@ -209,14 +214,6 @@
|
||||
"primaryCategory": "operation_experience",
|
||||
"navigationReadiness": "display_candidate_requires_access_or_nav_anchor_confirmation"
|
||||
},
|
||||
{
|
||||
"id": "supplemental_919d93c114",
|
||||
"name": "楼顶绿化",
|
||||
"sourceObjectName": "楼顶绿化",
|
||||
"floorId": "L3",
|
||||
"primaryCategory": "operation_experience",
|
||||
"navigationReadiness": "display_candidate_requires_access_or_nav_anchor_confirmation"
|
||||
},
|
||||
{
|
||||
"id": "supplemental_306932bd08",
|
||||
"name": "L1 室外水景",
|
||||
@@ -238,6 +235,7 @@
|
||||
"该 clean 包只保留前端应该读取的 canonical POI 数据,不再包含旧版 poi_all.json / poi_by_floor。",
|
||||
"data/poi_all.json 已是增强版 POI,不是旧 17 点版本。",
|
||||
"data/poi_by_floor/<floorId>.json 覆盖每个模型楼层,包括 L-1。",
|
||||
"补充的景观/公共空间点位来自模型 bbox center,适合展示和搜索;精确路线需要后续 NAV anchor。"
|
||||
"补充的景观/公共空间点位来自模型 bbox center,适合展示和搜索;精确路线需要后续 NAV anchor。",
|
||||
"本 strict_floor_prefix 包不会把无 L 前缀的外墙、建筑外围或装饰对象归入任何楼层 GLB。"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -3,11 +3,11 @@
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_audit",
|
||||
"outputRoot": "E:\\MyWork\\深圳国际艺术馆\\lender-museum\\evidence\\runs\\codex_nav_20260607_reloaded_223947\\app_nav_assets_v2_clean_20260609_075339",
|
||||
"outputRoot": "E:\\MyWork\\深圳国际艺术馆\\lender-museum\\evidence\\runs\\codex_nav_20260607_reloaded_223947\\app_nav_assets_v2_clean_20260611_093623",
|
||||
"checks": {
|
||||
"noLegacyPoiByFloorDir": true,
|
||||
"noLegacyEnhancedAlias": true,
|
||||
"canonicalPoiAllCount": 298,
|
||||
"canonicalPoiAllCount": 319,
|
||||
"allFloorPoiFilesExist": true,
|
||||
"floorIndexUsesCanonicalPoiByFloor": true,
|
||||
"nullPositionCount": 0,
|
||||
@@ -25,27 +25,27 @@
|
||||
"L4",
|
||||
"L5"
|
||||
],
|
||||
"overviewObjectCount": 45,
|
||||
"poiCount": 298,
|
||||
"sourceEnhancedPoiCount": 294,
|
||||
"supplementalPoiCount": 4,
|
||||
"overviewObjectCount": 5,
|
||||
"poiCount": 319,
|
||||
"sourceEnhancedPoiCount": 316,
|
||||
"supplementalPoiCount": 3,
|
||||
"poiCountByFloor": {
|
||||
"L-2": 52,
|
||||
"L-1": 46,
|
||||
"L1": 78,
|
||||
"L-2": 62,
|
||||
"L-1": 47,
|
||||
"L1": 81,
|
||||
"L1.5": 21,
|
||||
"L2": 47,
|
||||
"L3": 16,
|
||||
"L2": 55,
|
||||
"L3": 15,
|
||||
"L4": 27,
|
||||
"L5": 11
|
||||
},
|
||||
"poiCountByCategory": {
|
||||
"touring_poi": 17,
|
||||
"basic_service_facility": 47,
|
||||
"transport_circulation": 229,
|
||||
"touring_poi": 34,
|
||||
"basic_service_facility": 57,
|
||||
"transport_circulation": 232,
|
||||
"accessibility_special_service": 10,
|
||||
"safety_management": 0,
|
||||
"operation_experience": 8
|
||||
"operation_experience": 7
|
||||
},
|
||||
"connectorEndpointCount": 10
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-connector-endpoints-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:26:41.747324+00:00",
|
||||
"generatedAt": "2026-06-11T09:34:13.839693+00:00",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-index-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.466Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.719Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
@@ -9,25 +9,25 @@
|
||||
"floorId": "L-2",
|
||||
"order": -2,
|
||||
"modelAsset": "models_by_floor/L-2.glb",
|
||||
"objectCount": 55,
|
||||
"objectCount": 65,
|
||||
"poiDataAsset": "data/poi_by_floor/L-2.json",
|
||||
"poiCount": 52
|
||||
"poiCount": 62
|
||||
},
|
||||
{
|
||||
"floorId": "L-1",
|
||||
"order": -1,
|
||||
"modelAsset": "models_by_floor/L-1.glb",
|
||||
"objectCount": 49,
|
||||
"objectCount": 50,
|
||||
"poiDataAsset": "data/poi_by_floor/L-1.json",
|
||||
"poiCount": 46
|
||||
"poiCount": 47
|
||||
},
|
||||
{
|
||||
"floorId": "L1",
|
||||
"order": 1,
|
||||
"modelAsset": "models_by_floor/L1.glb",
|
||||
"objectCount": 90,
|
||||
"objectCount": 94,
|
||||
"poiDataAsset": "data/poi_by_floor/L1.json",
|
||||
"poiCount": 78
|
||||
"poiCount": 81
|
||||
},
|
||||
{
|
||||
"floorId": "L1.5",
|
||||
@@ -41,17 +41,17 @@
|
||||
"floorId": "L2",
|
||||
"order": 2,
|
||||
"modelAsset": "models_by_floor/L2.glb",
|
||||
"objectCount": 50,
|
||||
"objectCount": 58,
|
||||
"poiDataAsset": "data/poi_by_floor/L2.json",
|
||||
"poiCount": 47
|
||||
"poiCount": 55
|
||||
},
|
||||
{
|
||||
"floorId": "L3",
|
||||
"order": 3,
|
||||
"modelAsset": "models_by_floor/L3.glb",
|
||||
"objectCount": 22,
|
||||
"objectCount": 18,
|
||||
"poiDataAsset": "data/poi_by_floor/L3.json",
|
||||
"poiCount": 16
|
||||
"poiCount": 15
|
||||
},
|
||||
{
|
||||
"floorId": "L4",
|
||||
@@ -65,7 +65,7 @@
|
||||
"floorId": "L5",
|
||||
"order": 5,
|
||||
"modelAsset": "models_by_floor/L5.glb",
|
||||
"objectCount": 15,
|
||||
"objectCount": 14,
|
||||
"poiDataAsset": "data/poi_by_floor/L5.json",
|
||||
"poiCount": 11
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
id,name,floorId,primaryCategory,primaryCategoryZh,iconType,sourceObjectName,positionGltfX,positionGltfY,positionGltfZ,coordinateSource,navigationReadiness
|
||||
modelpoi_L-2_492e987660,L-2 母婴室,L-2,accessibility_special_service,无障碍与特殊人群服务,nursery,L-2_母婴室,-51.175003,-14.191206,39.344395,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_433ee81108,L-2 男卫生间01,L-2,basic_service_facility,基础服务设施,restroom,L-2_男卫生间01,-82.988602,-14.491209,43.190712,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_673c254e4d,L-2 男卫生间02,L-2,basic_service_facility,基础服务设施,restroom,L-2_男卫生间02,-18.808712,-14.491209,19.713522,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_1468d92dfd,L-2 男卫生间04,L-2,basic_service_facility,基础服务设施,restroom,L-2_男卫生间04,57.642273,-14.491209,24.258451,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a38a8c488e,L-2 女卫生间01,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间01,-91.456207,-14.491209,38.036762,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_b94325224a,L-2 女卫生间02,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间02,-30.663593,-14.491209,14.251428,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_e2cac3d857,L-2 女卫生间03,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间03,16.534239,-14.491209,25.273596,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_d36fe2dc9d,L-2 女卫生间03.001,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间03.001,32.573727,-14.491209,24.407684,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_c0855378b9,L-2 女卫生间04,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间04,68.159348,-14.491209,27.444523,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_492e987660,L-2 母婴室,L-2,accessibility_special_service,无障碍与特殊人群服务,nursery,L-2_母婴室,-51.175003,-13.147993,39.344395,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_433ee81108,L-2 男卫生间01,L-2,basic_service_facility,基础服务设施,restroom,L-2_男卫生间01,-82.988602,-13.447996,43.190712,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_673c254e4d,L-2 男卫生间02,L-2,basic_service_facility,基础服务设施,restroom,L-2_男卫生间02,-18.808712,-13.447996,19.713522,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_1468d92dfd,L-2 男卫生间04,L-2,basic_service_facility,基础服务设施,restroom,L-2_男卫生间04,57.642273,-13.447996,24.258451,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a38a8c488e,L-2 女卫生间01,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间01,-91.456207,-13.447996,38.036762,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_b94325224a,L-2 女卫生间02,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间02,-30.663593,-13.447996,14.251428,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_e2cac3d857,L-2 女卫生间03,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间03,16.534239,-13.447996,25.273596,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_d36fe2dc9d,L-2 女卫生间03.001,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间03.001,32.573727,-13.447996,24.407684,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_c0855378b9,L-2 女卫生间04,L-2,basic_service_facility,基础服务设施,restroom,L-2_女卫生间04,68.159348,-13.447996,27.444523,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0009,L-2 展厅1宇宙厅,L-2,touring_poi,游览 POI,exhibition,L-2_展厅1宇宙厅,-90.488953,-14.388401,44.977547,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L-2_1fa3680520,L-2 展厅1宇宙厅 展柜,L-2,touring_poi,游览 POI,exhibition,L-2_展厅1宇宙厅_展柜,-87.730865,-13.891212,54.098366,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_0328e49a57,L-2 展厅1宇宙厅 装饰,L-2,touring_poi,游览 POI,exhibition,L-2_展厅1宇宙厅_装饰,-90.488953,-11.498024,44.977547,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0010,L-2 展厅2地球厅,L-2,touring_poi,游览 POI,exhibition,L-2_展厅2地球厅,-17.07975,-14.388401,-8.416769,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L-2_e2ec8ce8ba,L-2 展厅2地球厅 展柜,L-2,touring_poi,游览 POI,exhibition,L-2_展厅2地球厅_展柜,-17.069817,-13.827784,-8.752005,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_f23816be84,L-2 展厅2地球厅 装饰,L-2,touring_poi,游览 POI,exhibition,L-2_展厅2地球厅_装饰,-17.07975,-11.498024,-8.416769,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0008,L-2 展厅3演化厅,L-2,touring_poi,游览 POI,exhibition,L-2_展厅3演化厅,25.952524,-14.388403,51.155842,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L-2_34bbb3d9d4,L-2 展厅3演化厅 展柜,L-2,touring_poi,游览 POI,exhibition,L-2_展厅3演化厅_展柜,25.310183,-13.89121,51.903332,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a2d90df88c,L-2 展厅3演化厅 装饰,L-2,touring_poi,游览 POI,exhibition,L-2_展厅3演化厅_装饰,25.952511,-11.48424,51.155872,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0007,L-2 展厅4恐龙厅,L-2,touring_poi,游览 POI,exhibition,L-2_展厅4恐龙厅,69.308327,-14.388403,-0.631475,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L-2_f877db497b,L-2 展厅4恐龙厅 展柜,L-2,touring_poi,游览 POI,exhibition,L-2_展厅4恐龙厅_展柜,69.237717,-13.987548,-0.634724,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_81c1403384,L-2 展厅4恐龙厅 装饰,L-2,touring_poi,游览 POI,exhibition,L-2_展厅4恐龙厅_装饰,70.205048,-7.689972,2.088411,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_ae5be493c0,L-2 电梯0001,L-2,transport_circulation,交通与通行动线,elevator,L-2_电梯0001,34.112198,-13.127687,77.432434,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_e628190832,L-2 电梯0002,L-2,transport_circulation,交通与通行动线,elevator,L-2_电梯0002,48.350098,-12.874514,67.133812,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a1322a6c4d,L-2 电梯0003,L-2,transport_circulation,交通与通行动线,elevator,L-2_电梯0003,90.601959,-12.863037,49.503075,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -32,25 +40,27 @@ modelpoi_L-2_a7f866a09a,L-2 电梯0017,L-2,transport_circulation,交通与通行
|
||||
modelpoi_L-2_0d88e71c27,L-2 电梯0018,L-2,transport_circulation,交通与通行动线,elevator,L-2_电梯0018,-99.716293,-12.873795,12.790838,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_ca23f27f00,L-2 电梯0019,L-2,transport_circulation,交通与通行动线,elevator,L-2_电梯0019,-100.377762,-12.873795,9.507727,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_73874acb68,L-2 扶梯,L-2,transport_circulation,交通与通行动线,escalator,L-2_扶梯,67.947716,-12.484815,42.180843,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_77bdede69c,L-2 楼梯0001,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0001,-113.980553,-14.191214,15.791812,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_d6275be404,L-2 楼梯0002,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0002,-125.09758,-14.191216,44.947601,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_d9df5eab1e,L-2 楼梯0003,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0003,-105.471481,-14.19121,-80.607361,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_3dd779ffc4,L-2 楼梯0004,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0004,-119.36657,-14.19121,-77.92852,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_bcfbe38b8c,L-2 楼梯0005,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0005,-118.673454,-14.191213,-19.434433,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_f5a71cd735,L-2 楼梯0006,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0006,-96.47963,-14.191214,1.494661,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_acacdf7cd3,L-2 楼梯0007,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0007,-34.926815,-14.191212,-27.636536,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_706e6e65ea,L-2 楼梯0008,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0008,-54.706528,-14.191216,59.089958,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_586f1dcb89,L-2 楼梯0009,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0009,-62.075691,-14.191199,69.846909,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_5e6d33b390,L-2 楼梯0010,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0010,-78.359177,-14.191216,79.284683,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a158651e42,L-2 楼梯0011,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0011,-51.635437,-14.191216,48.365948,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_4d987dbec1,L-2 楼梯0012,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0012,-2.737168,-14.191216,53.378433,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_ff21400c01,L-2 楼梯0013,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0013,91.222206,-14.191216,58.967903,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_990fd35b22,L-2 楼梯0014,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0014,93.295212,-14.191216,42.003674,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_49592f09b2,L-2 楼梯0015,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0015,94.548233,-14.191213,-5.112744,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a9c1ecac07,L-2 楼梯0016,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0016,46.435764,-14.191214,-3.948162,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_7a4fb7e3e7,L-2 楼梯0017,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0017,45.298622,-14.191214,4.789413,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_5250437db8,L-2 楼梯0018,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0018,53.007278,-14.191216,58.379272,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_84885548c6,L-2 停车位,L-2,transport_circulation,交通与通行动线,parking,L-2_停车位,-13.833179,-14.191204,10.672264,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_77bdede69c,L-2 楼梯0001,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0001,-113.980553,-13.148001,15.791812,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_d6275be404,L-2 楼梯0002,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0002,-125.09758,-13.148003,44.947601,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_d9df5eab1e,L-2 楼梯0003,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0003,-105.471481,-13.147997,-80.607361,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_3dd779ffc4,L-2 楼梯0004,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0004,-119.36657,-13.147997,-77.92852,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_bcfbe38b8c,L-2 楼梯0005,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0005,-118.673454,-13.148,-19.434433,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_f5a71cd735,L-2 楼梯0006,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0006,-96.47963,-13.148001,1.494661,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_acacdf7cd3,L-2 楼梯0007,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0007,-34.926815,-13.147999,-27.636536,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_706e6e65ea,L-2 楼梯0008,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0008,-54.706528,-13.148003,59.089958,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_586f1dcb89,L-2 楼梯0009,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0009,-62.075691,-13.147986,69.846909,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_5e6d33b390,L-2 楼梯0010,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0010,-78.359177,-13.148004,79.284683,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a158651e42,L-2 楼梯0011,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0011,-51.635437,-13.148003,48.365948,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_4d987dbec1,L-2 楼梯0012,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0012,-2.737168,-13.148003,53.378433,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_ff21400c01,L-2 楼梯0013,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0013,91.222206,-13.148003,58.967903,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_990fd35b22,L-2 楼梯0014,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0014,93.295212,-13.148003,42.003674,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_49592f09b2,L-2 楼梯0015,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0015,94.548233,-13.148,-5.112744,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_a9c1ecac07,L-2 楼梯0016,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0016,46.435764,-13.148001,-3.948162,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_7a4fb7e3e7,L-2 楼梯0017,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0017,45.298622,-13.148001,4.789413,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_5250437db8,L-2 楼梯0018,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0018,53.007278,-13.148003,58.379272,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_482544cba6,L-2 楼梯0019,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0019,-91.555466,-11.787357,46.893166,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_5753e93667,L-2 楼梯0020,L-2,transport_circulation,交通与通行动线,stair,L-2_楼梯0020,-77.065353,-11.787357,46.893166,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-2_84885548c6,L-2 停车位,L-2,transport_circulation,交通与通行动线,parking,L-2_停车位,-13.833179,-14.391253,10.672264,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_14ec89b181,L-1 球幕影院,L-1,touring_poi,游览 POI,theater,L-1_球幕影院,0.941269,-10.898361,-4.725914,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_e98e1e7579,L-1 电梯0001,L-1,transport_circulation,交通与通行动线,elevator,L-1_电梯0001,-99.961014,-9.296165,9.723492,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_31bfe24f01,L-1 电梯0002,L-1,transport_circulation,交通与通行动线,elevator,L-1_电梯0002,87.788162,-9.28541,49.793114,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -96,6 +106,7 @@ modelpoi_L-1_5b96f2b36e,L-1 楼梯0021,L-1,transport_circulation,交通与通行
|
||||
modelpoi_L-1_c2152945b5,L-1 楼梯0022,L-1,transport_circulation,交通与通行动线,stair,L-1_楼梯0022,-118.173111,-10.669845,-18.677877,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_d10fc5993f,L-1 楼梯0023,L-1,transport_circulation,交通与通行动线,stair,L-1_楼梯0023,-105.15239,-10.669839,-79.589508,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_7639982b98,L-1 楼梯0024,L-1,transport_circulation,交通与通行动线,stair,L-1_楼梯0024,-119.786385,-10.669839,-77.311356,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_04e400880b,L-1 楼梯0025,L-1,transport_circulation,交通与通行动线,stair,L-1_楼梯0025,-13.88942,-10.669846,-5.084072,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L-1_e3aff063cb,L-1 停车场地面,L-1,transport_circulation,交通与通行动线,parking,L-1_停车场地面,85.808472,-10.834019,67.684715,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_19555b8890,L1 轮椅及儿童车租赁,L1,accessibility_special_service,无障碍与特殊人群服务,rental,L1_轮椅及儿童车租赁,-65.994453,0.058651,15.326918,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_55e995bb4d,L1 母婴间,L1,accessibility_special_service,无障碍与特殊人群服务,nursery,L1_母婴间,-95.211792,0.058651,26.434822,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -111,10 +122,11 @@ modelpoi_L1_a89f999ce8,L1 男卫生间02,L1,basic_service_facility,基础服务
|
||||
modelpoi_L1_8045e85848,L1 女卫生间,L1,basic_service_facility,基础服务设施,restroom,L1_女卫生间,-70.903793,0.258651,24.592087,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_170ebd7038,L1 女卫生间01,L1,basic_service_facility,基础服务设施,restroom,L1_女卫生间01,131.002686,0.25865,38.265602,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_90b628a1d5,L1 女卫生间02,L1,basic_service_facility,基础服务设施,restroom,L1_女卫生间02,49.345047,0.258652,11.62169,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_11ae2666ee,L1 售票机,L1,basic_service_facility,基础服务设施,ticket,L1_售票机,17.294315,1,24.678955,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_f41223bb9c,L1 贵宾接待区,L1,operation_experience,运营体验类点位,vip,L1_贵宾接待区,-77.729813,0.058651,19.560249,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
supplemental_b23c543281,L1 室外绿化,L1,operation_experience,运营体验类点位,landscape,L1_室外绿化,3.996948,0.172545,-0.012264,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
supplemental_306932bd08,L1 室外水景,L1,operation_experience,运营体验类点位,photo,L1_室外水景,22.258747,-0.488044,-0.367207,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
supplemental_cc67428117,L1 室外下沉广场,L1,operation_experience,运营体验类点位,plaza,L1_室外下沉广场,31.983009,-2.810548,-12.300758,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
supplemental_b23c543281,L1 室外绿化,L1,operation_experience,运营体验类点位,landscape,L1_室外绿化,4.52948,0.172545,-0.231293,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
supplemental_306932bd08,L1 室外水景,L1,operation_experience,运营体验类点位,photo,L1_室外水景,23.06905,0.274697,-0.831573,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
supplemental_cc67428117,L1 室外下沉广场,L1,operation_experience,运营体验类点位,plaza,L1_室外下沉广场,32.247353,-2.810548,-12.300758,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
modelpoi_L1_350cd0e084,L1 动感多维影院,L1,touring_poi,游览 POI,theater,L1_动感多维影院,37.22998,0.058651,45.027328,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_9ba9a658bb,L1 巨幕影院,L1,touring_poi,游览 POI,theater,L1_巨幕影院,16.42691,0.058651,45.047546,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0003,L1 临展厅01,L1,touring_poi,游览 POI,exhibition,L1_临展厅01,-78.678642,0,-30.900698,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
@@ -122,6 +134,8 @@ poi_navpoi_0014,L1 临展厅02,L1,touring_poi,游览 POI,exhibition,L1_临展厅
|
||||
modelpoi_L1_d2b5d8b621,L1 球幕影院,L1,touring_poi,游览 POI,theater,L1_球幕影院,1.775749,3.377288,-5.777876,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0002,L1 展厅5人类厅,L1,touring_poi,游览 POI,exhibition,L1_展厅5人类厅,112.822556,-0.000004,51.974705,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L1_4e1111c7b1,L1 自然剧场,L1,touring_poi,游览 POI,theater,L1_自然剧场,24.2363,0.05865,65.833115,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_bce5578bda,L1 L1 展厅5人类厅 装饰01,L1,touring_poi,游览 POI,exhibition,L1_L1_展厅5人类厅_装饰01,112.835953,2.864931,51.190968,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_d6bfac9efe,L1 L1 展厅5人类厅 装饰02,L1,touring_poi,游览 POI,exhibition,L1_L1_展厅5人类厅_装饰02,112.655197,3.820072,51.249493,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_45de13b368,L1 电梯0001,L1,transport_circulation,交通与通行动线,elevator,L1_电梯0001,-100.345444,1.526052,9.748894,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_aae15a9bf4,L1 电梯0002,L1,transport_circulation,交通与通行动线,elevator,L1_电梯0002,86.457626,1.526052,9.073389,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_874fe33d28,L1 电梯0003,L1,transport_circulation,交通与通行动线,elevator,L1_电梯0003,79.746368,1.526052,17.265091,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -145,7 +159,6 @@ modelpoi_L1_d22f4e53ae,L1 电梯0020,L1,transport_circulation,交通与通行动
|
||||
modelpoi_L1_5338a76c2c,L1 电梯0021,L1,transport_circulation,交通与通行动线,elevator,L1_电梯0021,-99.683975,1.526052,13.032006,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_b3347e28c9,L1 扶梯,L1,transport_circulation,交通与通行动线,escalator,L1_扶梯,81.070969,-0.552914,36.499767,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_84fb77260b,L1 楼梯0001,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0001,-113.303513,0.217286,15.44129,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_a4a00c978d,L1 楼梯0002,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0002,-118.638206,0.217286,-19.517469,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_360cce5a19,L1 楼梯0003,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0003,-34.709263,0.300862,-27.007214,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_12b7990225,L1 楼梯0004,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0004,-51.784405,0.300859,43.113773,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_87efe86522,L1 楼梯0005,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0005,90.36853,0.296874,-5.492186,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -169,9 +182,10 @@ modelpoi_L1_4247dcb42a,L1 楼梯0022,L1,transport_circulation,交通与通行动
|
||||
modelpoi_L1_2ce36ed339,L1 楼梯0023,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0023,-123.082703,0.217286,29.087791,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_9461bfead0,L1 楼梯0024,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0024,-125.829529,0.217285,45.41563,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_8a9434da15,L1 楼梯0025,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0025,-73.533424,0.25,63.954227,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_9a31638f63,L1 楼梯0026,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0026,-127.951675,0.217286,-2.199766,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_fa423c9ad0,L1 楼梯0027,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0027,-104.784119,0.217286,-81.176064,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_7d0051d2a6,L1 楼梯0028,L1,transport_circulation,交通与通行动线,stair,L1_楼梯0028,-119.274986,0,-78.993362,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_5655c264c1,L1 室外楼梯0001,L1,transport_circulation,交通与通行动线,stair,L1_室外楼梯0001,-104.784119,0.217286,-81.176064,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_6fa8f94e0d,L1 室外楼梯0002,L1,transport_circulation,交通与通行动线,stair,L1_室外楼梯0002,-119.274986,0,-78.993362,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_9d38633ca9,L1 室外楼梯0003,L1,transport_circulation,交通与通行动线,stair,L1_室外楼梯0003,-118.638206,0.217286,-19.517469,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_3936c7e2b1,L1 室外楼梯0004,L1,transport_circulation,交通与通行动线,stair,L1_室外楼梯0004,-127.951675,0.217286,-2.199766,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_a6652685b8,L1 停车场,L1,transport_circulation,交通与通行动线,parking,L1_停车场,-126.124298,0,-16.088915,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_9611467f0f,L1 停车场入口,L1,transport_circulation,交通与通行动线,parking,L1_停车场入口,-117.349075,-2.64097,-76.993629,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L1_ce023b99e4,L1 停车场入口02,L1,transport_circulation,交通与通行动线,parking,L1_停车场入口02,72.57579,-4.072168,72.416092,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -202,10 +216,18 @@ modelpoi_L2_a5009070bb,L2 男卫生间001,L2,basic_service_facility,基础服务
|
||||
modelpoi_L2_2d14073a06,L2 男卫生间002,L2,basic_service_facility,基础服务设施,restroom,L2_男卫生间002,8.952654,10.273865,-1.457422,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_4470473302,L2 女卫生间001,L2,basic_service_facility,基础服务设施,restroom,L2_女卫生间001,-92.132439,10.558371,37.83683,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_e9fc021625,L2 女卫生间002,L2,basic_service_facility,基础服务设施,restroom,L2_女卫生间002,4.273729,10.273865,4.899143,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_9267c2d1d4,L2 卫生间,L2,basic_service_facility,基础服务设施,restroom,L2_卫生间,52.255562,10.273865,9.283119,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_f362d56eba,L2 文创店旗舰店,L2,basic_service_facility,基础服务设施,shop,L2_文创店旗舰店,-70.947243,10.178164,38.432304,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0006,L2 展厅 6生物厅,L2,touring_poi,游览 POI,exhibition,L2_展厅_6生物厅,69.074303,10.178165,1.496778,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L2_ecd476d1be,L2 展厅 6生物厅 展柜01,L2,touring_poi,游览 POI,exhibition,L2_展厅_6生物厅_展柜01,69.279694,10.281687,5.825405,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_d93b7e7a22,L2 展厅 6生物厅 展柜02,L2,touring_poi,游览 POI,exhibition,L2_展厅_6生物厅_展柜02,69.861053,10.594574,5.292932,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_bf6262807a,L2 展厅 6生物厅 装饰,L2,touring_poi,游览 POI,exhibition,L2_展厅_6生物厅_装饰,69.074303,13.479435,1.496778,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0005,L2 展厅 7生态厅,L2,touring_poi,游览 POI,exhibition,L2_展厅_7生态厅,24.894789,10.178165,53.795837,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L2_2fea6b6cff,L2 展厅 7生态厅 展柜,L2,touring_poi,游览 POI,exhibition,L2_展厅_7生态厅_展柜,25.018467,10.511989,53.729153,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_91de093d1e,L2 展厅 7生态厅 装饰,L2,touring_poi,游览 POI,exhibition,L2_展厅_7生态厅_装饰,24.894787,13.408146,53.795837,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
poi_navpoi_0004,L2 展厅 8家园厅,L2,touring_poi,游览 POI,exhibition,L2_展厅_8家园厅,-13.14613,10.178164,-10.430546,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L2_8b43a8a2a8,L2 展厅 8家园厅 展柜,L2,touring_poi,游览 POI,exhibition,L2_展厅_8家园厅_展柜,-12.998978,10.478165,-10.578304,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_83406e72e2,L2 展厅 8家园厅 装饰,L2,touring_poi,游览 POI,exhibition,L2_展厅_8家园厅_装饰,-13.14613,13.373477,-10.430546,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_4e0d3ab858,L2 电梯0001,L2,transport_circulation,交通与通行动线,elevator,L2_电梯0001,-100.345444,11.510569,9.748894,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_768d09579a,L2 电梯0002,L2,transport_circulation,交通与通行动线,elevator,L2_电梯0002,86.457626,11.510569,9.073389,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_9d3633cb8c,L2 电梯0003,L2,transport_circulation,交通与通行动线,elevator,L2_电梯0003,79.746368,11.510569,17.265091,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -222,17 +244,17 @@ modelpoi_L2_69b6036c36,L2 电梯0013,L2,transport_circulation,交通与通行动
|
||||
modelpoi_L2_50d761d668,L2 电梯0014,L2,transport_circulation,交通与通行动线,elevator,L2_电梯0014,-89.315796,11.510569,36.826317,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_8144b8df38,L2 电梯0015,L2,transport_circulation,交通与通行动线,elevator,L2_电梯0015,-88.703072,11.510569,33.007458,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_580aca14fe,L2 电梯0016,L2,transport_circulation,交通与通行动线,elevator,L2_电梯0016,-99.683975,11.510569,13.032006,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_9040c9507a,L2 电梯017,L2,transport_circulation,交通与通行动线,elevator,L2_电梯017,60.687622,11.510571,19.346493,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_041990743f,L2 扶梯001,L2,transport_circulation,交通与通行动线,escalator,L2_扶梯001,94.99762,8.514649,36.362118,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_5b3ad4268d,L2 楼梯0001,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0001,49.78883,10.178167,-2.614235,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_579220538a,L2 楼梯0002,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0002,47.912979,10.178167,-3.191393,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_579220538a,L2 楼梯0002,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0002,48.342949,10.178167,-2.291168,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_e8eb126078,L2 楼梯0003,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0003,95.642593,10.178164,58.023407,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_aacd33c979,L2 楼梯0004,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0004,89.312012,10.178167,1.252304,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_75beeb2694,L2 楼梯0005,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0005,87.619148,10.178167,-5.131519,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_dfc31aec44,L2 楼梯0006,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0006,47.859646,10.178166,3.881408,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_aacd33c979,L2 楼梯0004,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0004,89.507751,10.178167,1.233227,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_75beeb2694,L2 楼梯0005,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0005,87.892075,10.178167,-4.699806,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_dfc31aec44,L2 楼梯0006,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0006,47.717403,10.178167,3.410032,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_67c8408dca,L2 楼梯0007,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0007,41.546097,10.178164,69.148041,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_fbfebb3318,L2 楼梯0008,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0008,49.141712,10.178164,57.450394,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_600395b893,L2 楼梯0009,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0009,0.464776,10.178164,52.972031,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_220658455e,L2 楼梯0010,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0010,-4.680887,10.178166,10.44739,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_220658455e,L2 楼梯0010,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0010,-3.329174,10.178166,9.639655,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_2493f07479,L2 楼梯0011,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0011,-124.356628,10.178165,45.499649,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_f55d36b16e,L2 楼梯0012,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0012,-120.651779,10.178165,30.800243,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L2_8f3d7a0d43,L2 楼梯0013,L2,transport_circulation,交通与通行动线,stair,L2_楼梯0013,-111.083786,10.178166,18.195475,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
@@ -246,7 +268,6 @@ modelpoi_L2_50f2c5fe7f,L2 楼梯0020,L2,transport_circulation,交通与通行动
|
||||
poi_navpoi_0016,L3 无障碍卫生间,L3,accessibility_special_service,无障碍与特殊人群服务,accessibility,L3_无障碍卫生间,-88.936966,16.489281,42.595558,nav_data.navAnchors.policy_approved_machine_verified_nav_layer,nav_anchor_available
|
||||
modelpoi_L3_73737c95dc,L3 男卫生间,L3,basic_service_facility,基础服务设施,restroom,L3_男卫生间,-83.324158,16.540436,43.138031,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L3_7ead513df1,L3 女卫生间,L3,basic_service_facility,基础服务设施,restroom,L3_女卫生间,-91.718369,16.540436,37.972088,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
supplemental_919d93c114,楼顶绿化,L3,operation_experience,运营体验类点位,landscape,楼顶绿化,6.860313,16.841034,-5.471432,model_object_bbox_center_candidate,display_candidate_requires_access_or_nav_anchor_confirmation
|
||||
modelpoi_L3_293d59fa33,L3 电梯0001,L3,transport_circulation,交通与通行动线,elevator,L3_电梯0001,-100.076187,17.991472,12.459318,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L3_e311f0daf4,L3 电梯0002,L3,transport_circulation,交通与通行动线,elevator,L3_电梯0002,-100.57962,17.991472,9.362567,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
modelpoi_L3_4f70d09801,L3 电梯0003,L3,transport_circulation,交通与通行动线,elevator,L3_电梯0003,-81.503433,17.991472,30.988474,model_object_bbox_center_candidate,display_candidate_requires_nav_anchor_for_routing
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.446Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.694Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L-1",
|
||||
"poiCount": 46,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"poiCount": 47,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "modelpoi_L-1_14ec89b181",
|
||||
@@ -2753,6 +2753,67 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L-1_04e400880b",
|
||||
"name": "L-1 楼梯0025",
|
||||
"sourceObjectName": "L-1_楼梯0025",
|
||||
"floorId": "L-1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "stair",
|
||||
"iconType": "stair",
|
||||
"reason": "matched_stair_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
-13.88942,
|
||||
5.084072,
|
||||
-10.669846
|
||||
],
|
||||
"positionGltf": [
|
||||
-13.88942,
|
||||
-10.669846,
|
||||
-5.084072
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-15.016052,
|
||||
0.703743,
|
||||
-10.670072
|
||||
],
|
||||
"max": [
|
||||
-12.762787,
|
||||
9.464401,
|
||||
-10.66962
|
||||
],
|
||||
"center": [
|
||||
-13.88942,
|
||||
5.084072,
|
||||
-10.669846
|
||||
],
|
||||
"size": [
|
||||
2.253265,
|
||||
8.760658,
|
||||
0.000452
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L-1_e3aff063cb",
|
||||
"name": "L-1 停车场地面",
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.449Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.698Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L1.5",
|
||||
"poiCount": 21,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "poi_navpoi_0012",
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.447Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.696Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L1",
|
||||
"poiCount": 78,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"poiCount": 81,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "modelpoi_L1_19555b8890",
|
||||
@@ -854,6 +854,67 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_11ae2666ee",
|
||||
"name": "L1 售票机",
|
||||
"sourceObjectName": "L1_售票机",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "basic_service_facility",
|
||||
"primaryCategoryZh": "基础服务设施",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"subcategory": "ticket_check",
|
||||
"iconType": "ticket",
|
||||
"reason": "matched_ticket_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "ticket",
|
||||
"positionBlender": [
|
||||
17.294315,
|
||||
-24.678955,
|
||||
1
|
||||
],
|
||||
"positionGltf": [
|
||||
17.294315,
|
||||
1,
|
||||
24.678955
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
16.932575,
|
||||
-25.224066,
|
||||
0
|
||||
],
|
||||
"max": [
|
||||
17.656055,
|
||||
-24.133844,
|
||||
2
|
||||
],
|
||||
"center": [
|
||||
17.294315,
|
||||
-24.678955,
|
||||
1
|
||||
],
|
||||
"size": [
|
||||
0.72348,
|
||||
1.090221,
|
||||
2
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_f41223bb9c",
|
||||
"name": "L1 贵宾接待区",
|
||||
@@ -933,34 +994,34 @@
|
||||
],
|
||||
"iconType": "landscape",
|
||||
"positionBlender": [
|
||||
3.996948,
|
||||
0.012264,
|
||||
4.52948,
|
||||
0.231293,
|
||||
0.172545
|
||||
],
|
||||
"positionGltf": [
|
||||
3.996948,
|
||||
4.52948,
|
||||
0.172545,
|
||||
-0.012264
|
||||
-0.231293
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-139.051422,
|
||||
-105.240479,
|
||||
-139.421356,
|
||||
-104.762138,
|
||||
-0.077455
|
||||
],
|
||||
"max": [
|
||||
147.045319,
|
||||
105.265007,
|
||||
148.480316,
|
||||
105.224724,
|
||||
0.422545
|
||||
],
|
||||
"center": [
|
||||
3.996948,
|
||||
0.012264,
|
||||
4.52948,
|
||||
0.231293,
|
||||
0.172545
|
||||
],
|
||||
"size": [
|
||||
286.096741,
|
||||
210.505493,
|
||||
287.901672,
|
||||
209.986862,
|
||||
0.5
|
||||
]
|
||||
},
|
||||
@@ -968,7 +1029,7 @@
|
||||
"navigationReadiness": "display_candidate_requires_access_or_nav_anchor_confirmation",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object_supplemental_clean_package",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"floorInferenceMethod": "strict_object_name_floor_prefix",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
@@ -994,42 +1055,42 @@
|
||||
],
|
||||
"iconType": "photo",
|
||||
"positionBlender": [
|
||||
22.258747,
|
||||
0.367207,
|
||||
-0.488044
|
||||
23.06905,
|
||||
0.831573,
|
||||
0.274697
|
||||
],
|
||||
"positionGltf": [
|
||||
22.258747,
|
||||
-0.488044,
|
||||
-0.367207
|
||||
23.06905,
|
||||
0.274697,
|
||||
-0.831573
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-93.667229,
|
||||
-90.865891,
|
||||
-1.82078
|
||||
-92.856926,
|
||||
-90.40152,
|
||||
-0.329199
|
||||
],
|
||||
"max": [
|
||||
138.184723,
|
||||
91.600304,
|
||||
0.844692
|
||||
138.995026,
|
||||
92.064667,
|
||||
0.878593
|
||||
],
|
||||
"center": [
|
||||
22.258747,
|
||||
0.367207,
|
||||
-0.488044
|
||||
23.06905,
|
||||
0.831573,
|
||||
0.274697
|
||||
],
|
||||
"size": [
|
||||
231.851959,
|
||||
182.466187,
|
||||
2.665472
|
||||
1.207792
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_access_or_nav_anchor_confirmation",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object_supplemental_clean_package",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"floorInferenceMethod": "strict_object_name_floor_prefix",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
@@ -1062,12 +1123,12 @@
|
||||
],
|
||||
"iconType": "plaza",
|
||||
"positionBlender": [
|
||||
31.983009,
|
||||
32.247353,
|
||||
12.300758,
|
||||
-2.810548
|
||||
],
|
||||
"positionGltf": [
|
||||
31.983009,
|
||||
32.247353,
|
||||
-2.810548,
|
||||
-12.300758
|
||||
],
|
||||
@@ -1078,17 +1139,17 @@
|
||||
-6.257907
|
||||
],
|
||||
"max": [
|
||||
49.637825,
|
||||
50.166512,
|
||||
37.382774,
|
||||
0.63681
|
||||
],
|
||||
"center": [
|
||||
31.983009,
|
||||
32.247353,
|
||||
12.300758,
|
||||
-2.810548
|
||||
],
|
||||
"size": [
|
||||
35.309631,
|
||||
35.838318,
|
||||
50.164032,
|
||||
6.894717
|
||||
]
|
||||
@@ -1097,7 +1158,7 @@
|
||||
"navigationReadiness": "display_candidate_requires_access_or_nav_anchor_confirmation",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object_supplemental_clean_package",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"floorInferenceMethod": "strict_object_name_floor_prefix",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
@@ -1457,6 +1518,128 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_bce5578bda",
|
||||
"name": "L1 L1 展厅5人类厅 装饰01",
|
||||
"sourceObjectName": "L1_L1_展厅5人类厅_装饰01",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
112.835953,
|
||||
-51.190968,
|
||||
2.864931
|
||||
],
|
||||
"positionGltf": [
|
||||
112.835953,
|
||||
2.864931,
|
||||
51.190968
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
84.834236,
|
||||
-79.192871,
|
||||
0.034751
|
||||
],
|
||||
"max": [
|
||||
140.837662,
|
||||
-23.189064,
|
||||
5.695111
|
||||
],
|
||||
"center": [
|
||||
112.835953,
|
||||
-51.190968,
|
||||
2.864931
|
||||
],
|
||||
"size": [
|
||||
56.003426,
|
||||
56.003807,
|
||||
5.66036
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_d6bfac9efe",
|
||||
"name": "L1 L1 展厅5人类厅 装饰02",
|
||||
"sourceObjectName": "L1_L1_展厅5人类厅_装饰02",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
112.655197,
|
||||
-51.249493,
|
||||
3.820072
|
||||
],
|
||||
"positionGltf": [
|
||||
112.655197,
|
||||
3.820072,
|
||||
51.249493
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
108.68396,
|
||||
-55.220016,
|
||||
0.032804
|
||||
],
|
||||
"max": [
|
||||
116.626434,
|
||||
-47.278969,
|
||||
7.607341
|
||||
],
|
||||
"center": [
|
||||
112.655197,
|
||||
-51.249493,
|
||||
3.820072
|
||||
],
|
||||
"size": [
|
||||
7.942474,
|
||||
7.941048,
|
||||
7.574537
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_45de13b368",
|
||||
"name": "L1 电梯0001",
|
||||
@@ -2860,67 +3043,6 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_a4a00c978d",
|
||||
"name": "L1 楼梯0002",
|
||||
"sourceObjectName": "L1_楼梯0002",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "stair",
|
||||
"iconType": "stair",
|
||||
"reason": "matched_stair_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
-118.638206,
|
||||
19.517469,
|
||||
0.217286
|
||||
],
|
||||
"positionGltf": [
|
||||
-118.638206,
|
||||
0.217286,
|
||||
-19.517469
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-123.097366,
|
||||
17.564606,
|
||||
0.217286
|
||||
],
|
||||
"max": [
|
||||
-114.179047,
|
||||
21.470333,
|
||||
0.217286
|
||||
],
|
||||
"center": [
|
||||
-118.638206,
|
||||
19.517469,
|
||||
0.217286
|
||||
],
|
||||
"size": [
|
||||
8.91832,
|
||||
3.905727,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_360cce5a19",
|
||||
"name": "L1 楼梯0003",
|
||||
@@ -4325,70 +4447,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_9a31638f63",
|
||||
"name": "L1 楼梯0026",
|
||||
"sourceObjectName": "L1_楼梯0026",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "stair",
|
||||
"iconType": "stair",
|
||||
"reason": "matched_stair_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
-127.951675,
|
||||
2.199766,
|
||||
0.217286
|
||||
],
|
||||
"positionGltf": [
|
||||
-127.951675,
|
||||
0.217286,
|
||||
-2.199766
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-129.983459,
|
||||
-1.394757,
|
||||
0.217286
|
||||
],
|
||||
"max": [
|
||||
-125.919891,
|
||||
5.794289,
|
||||
0.217286
|
||||
],
|
||||
"center": [
|
||||
-127.951675,
|
||||
2.199766,
|
||||
0.217286
|
||||
],
|
||||
"size": [
|
||||
4.063568,
|
||||
7.189046,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_fa423c9ad0",
|
||||
"name": "L1 楼梯0027",
|
||||
"sourceObjectName": "L1_楼梯0027",
|
||||
"id": "modelpoi_L1_5655c264c1",
|
||||
"name": "L1 室外楼梯0001",
|
||||
"sourceObjectName": "L1_室外楼梯0001",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
@@ -4447,9 +4508,9 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_7d0051d2a6",
|
||||
"name": "L1 楼梯0028",
|
||||
"sourceObjectName": "L1_楼梯0028",
|
||||
"id": "modelpoi_L1_6fa8f94e0d",
|
||||
"name": "L1 室外楼梯0002",
|
||||
"sourceObjectName": "L1_室外楼梯0002",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
@@ -4507,6 +4568,128 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_9d38633ca9",
|
||||
"name": "L1 室外楼梯0003",
|
||||
"sourceObjectName": "L1_室外楼梯0003",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "stair",
|
||||
"iconType": "stair",
|
||||
"reason": "matched_stair_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
-118.638206,
|
||||
19.517469,
|
||||
0.217286
|
||||
],
|
||||
"positionGltf": [
|
||||
-118.638206,
|
||||
0.217286,
|
||||
-19.517469
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-123.097366,
|
||||
17.564606,
|
||||
0.217286
|
||||
],
|
||||
"max": [
|
||||
-114.179047,
|
||||
21.470333,
|
||||
0.217286
|
||||
],
|
||||
"center": [
|
||||
-118.638206,
|
||||
19.517469,
|
||||
0.217286
|
||||
],
|
||||
"size": [
|
||||
8.91832,
|
||||
3.905727,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_3936c7e2b1",
|
||||
"name": "L1 室外楼梯0004",
|
||||
"sourceObjectName": "L1_室外楼梯0004",
|
||||
"floorId": "L1",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "stair",
|
||||
"iconType": "stair",
|
||||
"reason": "matched_stair_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
-127.951675,
|
||||
2.199766,
|
||||
0.217286
|
||||
],
|
||||
"positionGltf": [
|
||||
-127.951675,
|
||||
0.217286,
|
||||
-2.199766
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-129.983459,
|
||||
-1.394757,
|
||||
0.217286
|
||||
],
|
||||
"max": [
|
||||
-125.919891,
|
||||
5.794289,
|
||||
0.217286
|
||||
],
|
||||
"center": [
|
||||
-127.951675,
|
||||
2.199766,
|
||||
0.217286
|
||||
],
|
||||
"size": [
|
||||
4.063568,
|
||||
7.189046,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L1_a6652685b8",
|
||||
"name": "L1 停车场",
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.450Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.699Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L2",
|
||||
"poiCount": 47,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"poiCount": 55,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "poi_navpoi_0015",
|
||||
@@ -356,6 +356,67 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_9267c2d1d4",
|
||||
"name": "L2 卫生间",
|
||||
"sourceObjectName": "L2_卫生间",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "basic_service_facility",
|
||||
"primaryCategoryZh": "基础服务设施",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"subcategory": "restroom",
|
||||
"iconType": "restroom",
|
||||
"reason": "matched_restroom_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "restroom",
|
||||
"positionBlender": [
|
||||
52.255562,
|
||||
-9.283119,
|
||||
10.273865
|
||||
],
|
||||
"positionGltf": [
|
||||
52.255562,
|
||||
10.273865,
|
||||
9.283119
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
50.174667,
|
||||
-11.629254,
|
||||
10.273865
|
||||
],
|
||||
"max": [
|
||||
54.336456,
|
||||
-6.936984,
|
||||
10.273865
|
||||
],
|
||||
"center": [
|
||||
52.255562,
|
||||
-9.283119,
|
||||
10.273865
|
||||
],
|
||||
"size": [
|
||||
4.161789,
|
||||
4.69227,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_f362d56eba",
|
||||
"name": "L2 文创店旗舰店",
|
||||
@@ -453,6 +514,203 @@
|
||||
"snappedWalkableId": "navwalk_0034",
|
||||
"duplicateOfAuthorityPoiId": null
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_ecd476d1be",
|
||||
"name": "L2 展厅 6生物厅 展柜01",
|
||||
"sourceObjectName": "L2_展厅_6生物厅_展柜01",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
},
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"subcategory": "locker",
|
||||
"iconType": "locker",
|
||||
"reason": "matched_locker_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
69.279694,
|
||||
-5.825405,
|
||||
10.281687
|
||||
],
|
||||
"positionGltf": [
|
||||
69.279694,
|
||||
10.281687,
|
||||
5.825405
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
62.97464,
|
||||
-12.536184,
|
||||
10.031687
|
||||
],
|
||||
"max": [
|
||||
75.584747,
|
||||
0.885374,
|
||||
10.531687
|
||||
],
|
||||
"center": [
|
||||
69.279694,
|
||||
-5.825405,
|
||||
10.281687
|
||||
],
|
||||
"size": [
|
||||
12.610107,
|
||||
13.421558,
|
||||
0.5
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_d93b7e7a22",
|
||||
"name": "L2 展厅 6生物厅 展柜02",
|
||||
"sourceObjectName": "L2_展厅_6生物厅_展柜02",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
},
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"subcategory": "locker",
|
||||
"iconType": "locker",
|
||||
"reason": "matched_locker_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
69.861053,
|
||||
-5.292932,
|
||||
10.594574
|
||||
],
|
||||
"positionGltf": [
|
||||
69.861053,
|
||||
10.594574,
|
||||
5.292932
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
63.916744,
|
||||
-9.808617,
|
||||
10.031687
|
||||
],
|
||||
"max": [
|
||||
75.805359,
|
||||
-0.777246,
|
||||
11.157462
|
||||
],
|
||||
"center": [
|
||||
69.861053,
|
||||
-5.292932,
|
||||
10.594574
|
||||
],
|
||||
"size": [
|
||||
11.888615,
|
||||
9.03137,
|
||||
1.125775
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_bf6262807a",
|
||||
"name": "L2 展厅 6生物厅 装饰",
|
||||
"sourceObjectName": "L2_展厅_6生物厅_装饰",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
69.074303,
|
||||
-1.496778,
|
||||
13.479435
|
||||
],
|
||||
"positionGltf": [
|
||||
69.074303,
|
||||
13.479435,
|
||||
1.496778
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
52.643074,
|
||||
-17.928007,
|
||||
13.479435
|
||||
],
|
||||
"max": [
|
||||
85.505531,
|
||||
14.934451,
|
||||
13.479435
|
||||
],
|
||||
"center": [
|
||||
69.074303,
|
||||
-1.496778,
|
||||
13.479435
|
||||
],
|
||||
"size": [
|
||||
32.862457,
|
||||
32.862457,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "poi_navpoi_0005",
|
||||
"name": "L2 展厅 7生态厅",
|
||||
@@ -489,6 +747,135 @@
|
||||
"snappedWalkableId": "navwalk_0033",
|
||||
"duplicateOfAuthorityPoiId": null
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_2fea6b6cff",
|
||||
"name": "L2 展厅 7生态厅 展柜",
|
||||
"sourceObjectName": "L2_展厅_7生态厅_展柜",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
},
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"subcategory": "locker",
|
||||
"iconType": "locker",
|
||||
"reason": "matched_locker_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
25.018467,
|
||||
-53.729153,
|
||||
10.511989
|
||||
],
|
||||
"positionGltf": [
|
||||
25.018467,
|
||||
10.511989,
|
||||
53.729153
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
6.264536,
|
||||
-72.165092,
|
||||
10.011989
|
||||
],
|
||||
"max": [
|
||||
43.772396,
|
||||
-35.293213,
|
||||
11.011989
|
||||
],
|
||||
"center": [
|
||||
25.018467,
|
||||
-53.729153,
|
||||
10.511989
|
||||
],
|
||||
"size": [
|
||||
37.507858,
|
||||
36.87188,
|
||||
1
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_91de093d1e",
|
||||
"name": "L2 展厅 7生态厅 装饰",
|
||||
"sourceObjectName": "L2_展厅_7生态厅_装饰",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
24.894787,
|
||||
-53.795837,
|
||||
13.408146
|
||||
],
|
||||
"positionGltf": [
|
||||
24.894787,
|
||||
13.408146,
|
||||
53.795837
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
6.307831,
|
||||
-72.382797,
|
||||
13.408146
|
||||
],
|
||||
"max": [
|
||||
43.481743,
|
||||
-35.208881,
|
||||
13.408146
|
||||
],
|
||||
"center": [
|
||||
24.894787,
|
||||
-53.795837,
|
||||
13.408146
|
||||
],
|
||||
"size": [
|
||||
37.173912,
|
||||
37.173916,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "poi_navpoi_0004",
|
||||
"name": "L2 展厅 8家园厅",
|
||||
@@ -525,6 +912,135 @@
|
||||
"snappedWalkableId": "navwalk_0032",
|
||||
"duplicateOfAuthorityPoiId": null
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_8b43a8a2a8",
|
||||
"name": "L2 展厅 8家园厅 展柜",
|
||||
"sourceObjectName": "L2_展厅_8家园厅_展柜",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
},
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"subcategory": "locker",
|
||||
"iconType": "locker",
|
||||
"reason": "matched_locker_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
-12.998978,
|
||||
10.578304,
|
||||
10.478165
|
||||
],
|
||||
"positionGltf": [
|
||||
-12.998978,
|
||||
10.478165,
|
||||
-10.578304
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-33.451897,
|
||||
-9.404156,
|
||||
9.978165
|
||||
],
|
||||
"max": [
|
||||
7.453941,
|
||||
30.560764,
|
||||
10.978165
|
||||
],
|
||||
"center": [
|
||||
-12.998978,
|
||||
10.578304,
|
||||
10.478165
|
||||
],
|
||||
"size": [
|
||||
40.905838,
|
||||
39.96492,
|
||||
1
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_83406e72e2",
|
||||
"name": "L2 展厅 8家园厅 装饰",
|
||||
"sourceObjectName": "L2_展厅_8家园厅_装饰",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "touring_poi",
|
||||
"primaryCategoryZh": "游览 POI",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"subcategory": "exhibition_hall",
|
||||
"iconType": "exhibition",
|
||||
"reason": "matched_exhibition_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "exhibition",
|
||||
"positionBlender": [
|
||||
-13.14613,
|
||||
10.430546,
|
||||
13.373477
|
||||
],
|
||||
"positionGltf": [
|
||||
-13.14613,
|
||||
13.373477,
|
||||
-10.430546
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-33.201469,
|
||||
-9.624794,
|
||||
13.373477
|
||||
],
|
||||
"max": [
|
||||
6.909211,
|
||||
30.485886,
|
||||
13.373477
|
||||
],
|
||||
"center": [
|
||||
-13.14613,
|
||||
10.430546,
|
||||
13.373477
|
||||
],
|
||||
"size": [
|
||||
40.11068,
|
||||
40.11068,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_4e0d3ab858",
|
||||
"name": "L2 电梯0001",
|
||||
@@ -1501,6 +2017,67 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_9040c9507a",
|
||||
"name": "L2 电梯017",
|
||||
"sourceObjectName": "L2_电梯017",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "elevator",
|
||||
"iconType": "elevator",
|
||||
"reason": "matched_elevator_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "elevator",
|
||||
"positionBlender": [
|
||||
60.687622,
|
||||
-19.346493,
|
||||
11.510571
|
||||
],
|
||||
"positionGltf": [
|
||||
60.687622,
|
||||
11.510571,
|
||||
19.346493
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
58.144585,
|
||||
-21.878351,
|
||||
10.008377
|
||||
],
|
||||
"max": [
|
||||
63.230656,
|
||||
-16.814634,
|
||||
13.012766
|
||||
],
|
||||
"center": [
|
||||
60.687622,
|
||||
-19.346493,
|
||||
11.510571
|
||||
],
|
||||
"size": [
|
||||
5.086071,
|
||||
5.063717,
|
||||
3.004389
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_041990743f",
|
||||
"name": "L2 扶梯001",
|
||||
@@ -1562,67 +2139,6 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_5b3ad4268d",
|
||||
"name": "L2 楼梯0001",
|
||||
"sourceObjectName": "L2_楼梯0001",
|
||||
"floorId": "L2",
|
||||
"primaryCategory": "transport_circulation",
|
||||
"primaryCategoryZh": "交通与通行动线",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"subcategory": "stair",
|
||||
"iconType": "stair",
|
||||
"reason": "matched_stair_keywords"
|
||||
}
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
49.78883,
|
||||
2.614235,
|
||||
10.178167
|
||||
],
|
||||
"positionGltf": [
|
||||
49.78883,
|
||||
10.178167,
|
||||
-2.614235
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
48.060421,
|
||||
0.55634,
|
||||
10.178167
|
||||
],
|
||||
"max": [
|
||||
51.517239,
|
||||
4.67213,
|
||||
10.178167
|
||||
],
|
||||
"center": [
|
||||
49.78883,
|
||||
2.614235,
|
||||
10.178167
|
||||
],
|
||||
"size": [
|
||||
3.456818,
|
||||
4.115789,
|
||||
0
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_nav_anchor_for_routing",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object",
|
||||
"floorInferenceMethod": "name_or_collection_or_custom_property",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位从当前 Blender 模型对象名和 bbox center 提取,适合前端搜索、筛选和初步展示。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L2_579220538a",
|
||||
"name": "L2 楼梯0002",
|
||||
@@ -1641,34 +2157,34 @@
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
47.912979,
|
||||
3.191393,
|
||||
48.342949,
|
||||
2.291168,
|
||||
10.178167
|
||||
],
|
||||
"positionGltf": [
|
||||
47.912979,
|
||||
48.342949,
|
||||
10.178167,
|
||||
-3.191393
|
||||
-2.291168
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
46.033844,
|
||||
1.109414,
|
||||
44.615009,
|
||||
-1.112592,
|
||||
10.178167
|
||||
],
|
||||
"max": [
|
||||
49.792118,
|
||||
5.273373,
|
||||
52.070892,
|
||||
5.694929,
|
||||
10.178167
|
||||
],
|
||||
"center": [
|
||||
47.912979,
|
||||
3.191393,
|
||||
48.342949,
|
||||
2.291168,
|
||||
10.178167
|
||||
],
|
||||
"size": [
|
||||
3.758274,
|
||||
4.163959,
|
||||
7.455883,
|
||||
6.80752,
|
||||
0
|
||||
]
|
||||
},
|
||||
@@ -1763,34 +2279,34 @@
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
89.312012,
|
||||
-1.252304,
|
||||
89.507751,
|
||||
-1.233227,
|
||||
10.178167
|
||||
],
|
||||
"positionGltf": [
|
||||
89.312012,
|
||||
89.507751,
|
||||
10.178167,
|
||||
1.252304
|
||||
1.233227
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
86.608589,
|
||||
-4.589386,
|
||||
86.639137,
|
||||
-4.504424,
|
||||
10.178167
|
||||
],
|
||||
"max": [
|
||||
92.015442,
|
||||
2.084779,
|
||||
92.376358,
|
||||
2.037969,
|
||||
10.178167
|
||||
],
|
||||
"center": [
|
||||
89.312012,
|
||||
-1.252304,
|
||||
89.507751,
|
||||
-1.233227,
|
||||
10.178167
|
||||
],
|
||||
"size": [
|
||||
5.406853,
|
||||
6.674165,
|
||||
5.737221,
|
||||
6.542393,
|
||||
0
|
||||
]
|
||||
},
|
||||
@@ -1824,34 +2340,34 @@
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
87.619148,
|
||||
5.131519,
|
||||
87.892075,
|
||||
4.699806,
|
||||
10.178167
|
||||
],
|
||||
"positionGltf": [
|
||||
87.619148,
|
||||
87.892075,
|
||||
10.178167,
|
||||
-5.131519
|
||||
-4.699806
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
85.311478,
|
||||
2.042627,
|
||||
85.100731,
|
||||
0.739156,
|
||||
10.178167
|
||||
],
|
||||
"max": [
|
||||
89.926819,
|
||||
8.220411,
|
||||
90.683418,
|
||||
8.660455,
|
||||
10.178167
|
||||
],
|
||||
"center": [
|
||||
87.619148,
|
||||
5.131519,
|
||||
87.892075,
|
||||
4.699806,
|
||||
10.178167
|
||||
],
|
||||
"size": [
|
||||
4.615341,
|
||||
6.177784,
|
||||
5.582687,
|
||||
7.921299,
|
||||
0
|
||||
]
|
||||
},
|
||||
@@ -1885,34 +2401,34 @@
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
47.859646,
|
||||
-3.881408,
|
||||
10.178166
|
||||
47.717403,
|
||||
-3.410032,
|
||||
10.178167
|
||||
],
|
||||
"positionGltf": [
|
||||
47.859646,
|
||||
10.178166,
|
||||
3.881408
|
||||
47.717403,
|
||||
10.178167,
|
||||
3.410032
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
45.350086,
|
||||
-7.03454,
|
||||
10.178166
|
||||
44.856789,
|
||||
-7.097251,
|
||||
10.178167
|
||||
],
|
||||
"max": [
|
||||
50.369205,
|
||||
-0.728276,
|
||||
10.178166
|
||||
50.578018,
|
||||
0.277186,
|
||||
10.178167
|
||||
],
|
||||
"center": [
|
||||
47.859646,
|
||||
-3.881408,
|
||||
10.178166
|
||||
47.717403,
|
||||
-3.410032,
|
||||
10.178167
|
||||
],
|
||||
"size": [
|
||||
5.019119,
|
||||
6.306264,
|
||||
5.72123,
|
||||
7.374437,
|
||||
0
|
||||
]
|
||||
},
|
||||
@@ -2129,34 +2645,34 @@
|
||||
],
|
||||
"iconType": "stair",
|
||||
"positionBlender": [
|
||||
-4.680887,
|
||||
-10.44739,
|
||||
-3.329174,
|
||||
-9.639655,
|
||||
10.178166
|
||||
],
|
||||
"positionGltf": [
|
||||
-4.680887,
|
||||
-3.329174,
|
||||
10.178166,
|
||||
10.44739
|
||||
9.639655
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-7.049561,
|
||||
-12.861378,
|
||||
-7.459712,
|
||||
-13.094296,
|
||||
10.178166
|
||||
],
|
||||
"max": [
|
||||
-2.312214,
|
||||
-8.033401,
|
||||
0.801365,
|
||||
-6.185015,
|
||||
10.178166
|
||||
],
|
||||
"center": [
|
||||
-4.680887,
|
||||
-10.44739,
|
||||
-3.329174,
|
||||
-9.639655,
|
||||
10.178166
|
||||
],
|
||||
"size": [
|
||||
4.737347,
|
||||
4.827976,
|
||||
8.261077,
|
||||
6.909281,
|
||||
0
|
||||
]
|
||||
},
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.451Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.700Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L3",
|
||||
"poiCount": 16,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"poiCount": 15,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "poi_navpoi_0016",
|
||||
@@ -173,67 +173,6 @@
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "supplemental_919d93c114",
|
||||
"name": "楼顶绿化",
|
||||
"sourceObjectName": "楼顶绿化",
|
||||
"floorId": "L3",
|
||||
"primaryCategory": "operation_experience",
|
||||
"primaryCategoryZh": "运营体验类点位",
|
||||
"categories": [
|
||||
{
|
||||
"topCategory": "operation_experience",
|
||||
"topCategoryZh": "运营体验类点位",
|
||||
"subcategory": "landscape_area",
|
||||
"iconType": "landscape",
|
||||
"reason": "clean_package_supplemental_rooftop_landscape_keyword"
|
||||
}
|
||||
],
|
||||
"iconType": "landscape",
|
||||
"positionBlender": [
|
||||
6.860313,
|
||||
5.471432,
|
||||
16.841034
|
||||
],
|
||||
"positionGltf": [
|
||||
6.860313,
|
||||
16.841034,
|
||||
-5.471432
|
||||
],
|
||||
"bboxBlender": {
|
||||
"min": [
|
||||
-126.819077,
|
||||
-81.523155,
|
||||
-0.851679
|
||||
],
|
||||
"max": [
|
||||
140.539703,
|
||||
92.466019,
|
||||
34.533749
|
||||
],
|
||||
"center": [
|
||||
6.860313,
|
||||
5.471432,
|
||||
16.841034
|
||||
],
|
||||
"size": [
|
||||
267.358765,
|
||||
173.989166,
|
||||
35.385429
|
||||
]
|
||||
},
|
||||
"coordinateSource": "model_object_bbox_center_candidate",
|
||||
"navigationReadiness": "display_candidate_requires_access_or_nav_anchor_confirmation",
|
||||
"sourceConfidence": "medium",
|
||||
"sourceType": "current_blender_scene_model_object_supplemental_clean_package",
|
||||
"floorInferenceMethod": "bbox_center_z_heuristic",
|
||||
"currentSceneObjectFound": true,
|
||||
"duplicateOfAuthorityPoiId": null,
|
||||
"notesZh": [
|
||||
"该点位是清洁资源包补充的景观/公共空间候选,适合前端展示、筛选和搜索。",
|
||||
"如需用于精确路线规划,需要后续补充或确认 NAV anchor 与开放状态。"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "modelpoi_L3_293d59fa33",
|
||||
"name": "L3 电梯0001",
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.452Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.701Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L4",
|
||||
"poiCount": 27,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "poi_navpoi_0017",
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-floor-poi-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.453Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.703Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
"floorId": "L5",
|
||||
"poiCount": 11,
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois",
|
||||
"sourceInvocation": "clean_app_nav_assets_v2_from_enhanced_model_pois_strict_floor_prefix",
|
||||
"pois": [
|
||||
{
|
||||
"id": "poi_navpoi_0013",
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schemaVersion": "miniapp-poi-category-index-clean-2.0",
|
||||
"generatedAt": "2026-06-09T07:53:40.463Z",
|
||||
"generatedAt": "2026-06-11T09:36:23.714Z",
|
||||
"runId": "codex_nav_20260607_reloaded_223947",
|
||||
"workflowRunId": "codex_nav_20260607_reloaded_223947",
|
||||
"status": "pass",
|
||||
@@ -8,12 +8,20 @@
|
||||
{
|
||||
"topCategory": "touring_poi",
|
||||
"topCategoryZh": "游览 POI",
|
||||
"poiCount": 17,
|
||||
"poiCount": 34,
|
||||
"poiIds": [
|
||||
"poi_navpoi_0009",
|
||||
"modelpoi_L-2_1fa3680520",
|
||||
"modelpoi_L-2_0328e49a57",
|
||||
"poi_navpoi_0010",
|
||||
"modelpoi_L-2_e2ec8ce8ba",
|
||||
"modelpoi_L-2_f23816be84",
|
||||
"poi_navpoi_0008",
|
||||
"modelpoi_L-2_34bbb3d9d4",
|
||||
"modelpoi_L-2_a2d90df88c",
|
||||
"poi_navpoi_0007",
|
||||
"modelpoi_L-2_f877db497b",
|
||||
"modelpoi_L-2_81c1403384",
|
||||
"modelpoi_L-1_14ec89b181",
|
||||
"modelpoi_L1_350cd0e084",
|
||||
"modelpoi_L1_9ba9a658bb",
|
||||
@@ -22,9 +30,18 @@
|
||||
"modelpoi_L1_d2b5d8b621",
|
||||
"poi_navpoi_0002",
|
||||
"modelpoi_L1_4e1111c7b1",
|
||||
"modelpoi_L1_bce5578bda",
|
||||
"modelpoi_L1_d6bfac9efe",
|
||||
"poi_navpoi_0006",
|
||||
"modelpoi_L2_ecd476d1be",
|
||||
"modelpoi_L2_d93b7e7a22",
|
||||
"modelpoi_L2_bf6262807a",
|
||||
"poi_navpoi_0005",
|
||||
"modelpoi_L2_2fea6b6cff",
|
||||
"modelpoi_L2_91de093d1e",
|
||||
"poi_navpoi_0004",
|
||||
"modelpoi_L2_8b43a8a2a8",
|
||||
"modelpoi_L2_83406e72e2",
|
||||
"modelpoi_L4_4ba6b2c3cd",
|
||||
"modelpoi_L4_a306ac4979"
|
||||
]
|
||||
@@ -32,7 +49,7 @@
|
||||
{
|
||||
"topCategory": "basic_service_facility",
|
||||
"topCategoryZh": "基础服务设施",
|
||||
"poiCount": 47,
|
||||
"poiCount": 57,
|
||||
"poiIds": [
|
||||
"modelpoi_L-2_492e987660",
|
||||
"modelpoi_L-2_433ee81108",
|
||||
@@ -43,6 +60,10 @@
|
||||
"modelpoi_L-2_e2cac3d857",
|
||||
"modelpoi_L-2_d36fe2dc9d",
|
||||
"modelpoi_L-2_c0855378b9",
|
||||
"modelpoi_L-2_1fa3680520",
|
||||
"modelpoi_L-2_e2ec8ce8ba",
|
||||
"modelpoi_L-2_34bbb3d9d4",
|
||||
"modelpoi_L-2_f877db497b",
|
||||
"modelpoi_L1_55e995bb4d",
|
||||
"poi_navpoi_0001",
|
||||
"poi_navpoi_0011",
|
||||
@@ -56,6 +77,7 @@
|
||||
"modelpoi_L1_8045e85848",
|
||||
"modelpoi_L1_170ebd7038",
|
||||
"modelpoi_L1_90b628a1d5",
|
||||
"modelpoi_L1_11ae2666ee",
|
||||
"poi_navpoi_0012",
|
||||
"modelpoi_L1.5_b21db95ba5",
|
||||
"modelpoi_L1.5_a5b10d8b77",
|
||||
@@ -65,7 +87,12 @@
|
||||
"modelpoi_L2_2d14073a06",
|
||||
"modelpoi_L2_4470473302",
|
||||
"modelpoi_L2_e9fc021625",
|
||||
"modelpoi_L2_9267c2d1d4",
|
||||
"modelpoi_L2_f362d56eba",
|
||||
"modelpoi_L2_ecd476d1be",
|
||||
"modelpoi_L2_d93b7e7a22",
|
||||
"modelpoi_L2_2fea6b6cff",
|
||||
"modelpoi_L2_8b43a8a2a8",
|
||||
"poi_navpoi_0016",
|
||||
"modelpoi_L3_73737c95dc",
|
||||
"modelpoi_L3_7ead513df1",
|
||||
@@ -86,7 +113,7 @@
|
||||
{
|
||||
"topCategory": "transport_circulation",
|
||||
"topCategoryZh": "交通与通行动线",
|
||||
"poiCount": 229,
|
||||
"poiCount": 232,
|
||||
"poiIds": [
|
||||
"modelpoi_L-2_ae5be493c0",
|
||||
"modelpoi_L-2_e628190832",
|
||||
@@ -126,6 +153,8 @@
|
||||
"modelpoi_L-2_a9c1ecac07",
|
||||
"modelpoi_L-2_7a4fb7e3e7",
|
||||
"modelpoi_L-2_5250437db8",
|
||||
"modelpoi_L-2_482544cba6",
|
||||
"modelpoi_L-2_5753e93667",
|
||||
"modelpoi_L-2_84885548c6",
|
||||
"modelpoi_L-1_e98e1e7579",
|
||||
"modelpoi_L-1_31bfe24f01",
|
||||
@@ -171,6 +200,7 @@
|
||||
"modelpoi_L-1_c2152945b5",
|
||||
"modelpoi_L-1_d10fc5993f",
|
||||
"modelpoi_L-1_7639982b98",
|
||||
"modelpoi_L-1_04e400880b",
|
||||
"modelpoi_L-1_e3aff063cb",
|
||||
"supplemental_cc67428117",
|
||||
"modelpoi_L1_45de13b368",
|
||||
@@ -196,7 +226,6 @@
|
||||
"modelpoi_L1_5338a76c2c",
|
||||
"modelpoi_L1_b3347e28c9",
|
||||
"modelpoi_L1_84fb77260b",
|
||||
"modelpoi_L1_a4a00c978d",
|
||||
"modelpoi_L1_360cce5a19",
|
||||
"modelpoi_L1_12b7990225",
|
||||
"modelpoi_L1_87efe86522",
|
||||
@@ -220,9 +249,10 @@
|
||||
"modelpoi_L1_2ce36ed339",
|
||||
"modelpoi_L1_9461bfead0",
|
||||
"modelpoi_L1_8a9434da15",
|
||||
"modelpoi_L1_9a31638f63",
|
||||
"modelpoi_L1_fa423c9ad0",
|
||||
"modelpoi_L1_7d0051d2a6",
|
||||
"modelpoi_L1_5655c264c1",
|
||||
"modelpoi_L1_6fa8f94e0d",
|
||||
"modelpoi_L1_9d38633ca9",
|
||||
"modelpoi_L1_3936c7e2b1",
|
||||
"modelpoi_L1_a6652685b8",
|
||||
"modelpoi_L1_9611467f0f",
|
||||
"modelpoi_L1_ce023b99e4",
|
||||
@@ -260,8 +290,8 @@
|
||||
"modelpoi_L2_50d761d668",
|
||||
"modelpoi_L2_8144b8df38",
|
||||
"modelpoi_L2_580aca14fe",
|
||||
"modelpoi_L2_9040c9507a",
|
||||
"modelpoi_L2_041990743f",
|
||||
"modelpoi_L2_5b3ad4268d",
|
||||
"modelpoi_L2_579220538a",
|
||||
"modelpoi_L2_e8eb126078",
|
||||
"modelpoi_L2_aacd33c979",
|
||||
@@ -345,7 +375,7 @@
|
||||
{
|
||||
"topCategory": "operation_experience",
|
||||
"topCategoryZh": "运营体验类点位",
|
||||
"poiCount": 8,
|
||||
"poiCount": 7,
|
||||
"poiIds": [
|
||||
"modelpoi_L1_19555b8890",
|
||||
"modelpoi_L1_e1f290ee5a",
|
||||
@@ -353,8 +383,7 @@
|
||||
"modelpoi_L1_f41223bb9c",
|
||||
"supplemental_b23c543281",
|
||||
"supplemental_306932bd08",
|
||||
"supplemental_cc67428117",
|
||||
"supplemental_919d93c114"
|
||||
"supplemental_cc67428117"
|
||||
]
|
||||
}
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user