feat: load remote map config and constrain tile bounds

This commit is contained in:
2026-03-20 16:19:12 +08:00
parent 8e6291885d
commit 1ecb4809df
8 changed files with 552 additions and 15 deletions

View File

@@ -3,12 +3,13 @@ import { CompassHeadingController } from '../sensor/compassHeadingController'
import { WebGLMapRenderer } from '../renderer/webglMapRenderer'
import { type MapRendererStats } from '../renderer/mapRenderer'
import { worldTileToLonLat, type LonLatPoint } from '../../utils/projection'
import { type RemoteMapConfig, type TileZoomBounds } from '../../utils/remoteMapConfig'
const RENDER_MODE = 'Single WebGL Pipeline'
const PROJECTION_MODE = 'WGS84 -> WorldTile -> Camera -> Screen'
const MAP_NORTH_OFFSET_DEG = 0
const MAGNETIC_DECLINATION_DEG = -6.91
const MAGNETIC_DECLINATION_TEXT = '6.91° W'
let MAGNETIC_DECLINATION_DEG = -6.91
let MAGNETIC_DECLINATION_TEXT = '6.91° W'
const MIN_ZOOM = 15
const MAX_ZOOM = 20
const DEFAULT_ZOOM = 17
@@ -70,6 +71,7 @@ export interface MapEngineViewState {
mapReady: boolean
mapReadyText: string
mapName: string
configStatusText: string
zoom: number
rotationDeg: number
rotationText: string
@@ -119,6 +121,7 @@ const VIEW_SYNC_KEYS: Array<keyof MapEngineViewState> = [
'mapReady',
'mapReadyText',
'mapName',
'configStatusText',
'zoom',
'rotationDeg',
'rotationText',
@@ -383,6 +386,12 @@ export class MapEngine {
autoRotateSourceMode: AutoRotateSourceMode
autoRotateCalibrationOffsetDeg: number | null
autoRotateCalibrationPending: boolean
minZoom: number
maxZoom: number
defaultZoom: number
defaultCenterTileX: number
defaultCenterTileY: number
tileBoundsByZoom: Record<number, TileZoomBounds> | null
constructor(buildVersion: string, callbacks: MapEngineCallbacks) {
this.buildVersion = buildVersion
@@ -405,6 +414,12 @@ export class MapEngine {
this.handleCompassError(message)
},
})
this.minZoom = MIN_ZOOM
this.maxZoom = MAX_ZOOM
this.defaultZoom = DEFAULT_ZOOM
this.defaultCenterTileX = DEFAULT_CENTER_TILE_X
this.defaultCenterTileY = DEFAULT_CENTER_TILE_Y
this.tileBoundsByZoom = null
this.state = {
buildVersion: this.buildVersion,
renderMode: RENDER_MODE,
@@ -412,6 +427,7 @@ export class MapEngine {
mapReady: false,
mapReadyText: 'BOOTING',
mapName: 'LCX 测试地图',
configStatusText: '远程配置待加载',
zoom: DEFAULT_ZOOM,
rotationDeg: 0,
rotationText: formatRotationText(0),
@@ -526,6 +542,55 @@ export class MapEngine {
this.compassController.start()
}
applyRemoteMapConfig(config: RemoteMapConfig): void {
MAGNETIC_DECLINATION_DEG = config.magneticDeclinationDeg
MAGNETIC_DECLINATION_TEXT = config.magneticDeclinationText
this.minZoom = config.minZoom
this.maxZoom = config.maxZoom
this.defaultZoom = config.defaultZoom
this.defaultCenterTileX = config.initialCenterTileX
this.defaultCenterTileY = config.initialCenterTileY
this.tileBoundsByZoom = config.tileBoundsByZoom
const statePatch: Partial<MapEngineViewState> = {
configStatusText: '远程配置已载入',
projectionMode: config.projectionModeText,
tileSource: config.tileSource,
sensorHeadingText: formatHeadingText(this.smoothedSensorHeadingDeg === null ? null : getCompassReferenceHeadingDeg(this.northReferenceMode, this.smoothedSensorHeadingDeg)),
compassDeclinationText: formatCompassDeclinationText(this.northReferenceMode),
northReferenceButtonText: formatNorthReferenceButtonText(this.northReferenceMode),
northReferenceText: formatNorthReferenceText(this.northReferenceMode),
compassNeedleDeg: formatCompassNeedleDegForMode(this.northReferenceMode, this.smoothedSensorHeadingDeg),
}
if (!this.state.stageWidth || !this.state.stageHeight) {
this.setState({
...statePatch,
zoom: this.defaultZoom,
centerTileX: this.defaultCenterTileX,
centerTileY: this.defaultCenterTileY,
centerText: buildCenterText(this.defaultZoom, this.defaultCenterTileX, this.defaultCenterTileY),
statusText: `远程地图配置已载入 (${this.buildVersion})`,
}, true)
return
}
this.commitViewport({
...statePatch,
zoom: this.defaultZoom,
centerTileX: this.defaultCenterTileX,
centerTileY: this.defaultCenterTileY,
tileTranslateX: 0,
tileTranslateY: 0,
}, `远程地图配置已载入 (${this.buildVersion})`, true, () => {
this.resetPreviewState()
this.syncRenderer()
if (this.state.orientationMode === 'heading-up' && this.refreshAutoRotateTarget()) {
this.scheduleAutoRotate()
}
})
}
handleTouchStart(event: WechatMiniprogram.TouchEvent): void {
this.clearInertiaTimer()
this.clearPreviewResetTimer()
@@ -696,9 +761,9 @@ export class MapEngine {
this.renderer.setAnimationPaused(false)
this.commitViewport(
{
zoom: DEFAULT_ZOOM,
centerTileX: DEFAULT_CENTER_TILE_X,
centerTileY: DEFAULT_CENTER_TILE_Y,
zoom: this.defaultZoom,
centerTileX: this.defaultCenterTileX,
centerTileY: this.defaultCenterTileY,
tileTranslateX: 0,
tileTranslateY: 0,
},
@@ -1196,6 +1261,7 @@ export class MapEngine {
zoom: this.state.zoom,
centerTileX: this.state.centerTileX,
centerTileY: this.state.centerTileY,
tileBoundsByZoom: this.tileBoundsByZoom,
viewportWidth: this.state.stageWidth,
viewportHeight: this.state.stageHeight,
visibleColumns: DESIRED_VISIBLE_COLUMNS,
@@ -1482,7 +1548,7 @@ export class MapEngine {
}
zoomAroundPoint(zoomDelta: number, stageX: number, stageY: number, residualScale: number): void {
const nextZoom = clamp(this.state.zoom + zoomDelta, MIN_ZOOM, MAX_ZOOM)
const nextZoom = clamp(this.state.zoom + zoomDelta, this.minZoom, this.maxZoom)
const appliedDelta = nextZoom - this.state.zoom
if (!appliedDelta) {
this.animatePreviewToRest()
@@ -1584,3 +1650,4 @@ export class MapEngine {