gpsLayer.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. import { calibratedLonLatToWorldTile } from '../../utils/projection'
  2. import { worldToScreen, type CameraState } from '../camera/camera'
  3. import { type MapLayer, type LayerRenderContext } from './mapLayer'
  4. import { type MapScene } from '../renderer/mapRenderer'
  5. import { type ScreenPoint } from './trackLayer'
  6. function buildVectorCamera(scene: MapScene): CameraState {
  7. return {
  8. centerWorldX: scene.exactCenterWorldX,
  9. centerWorldY: scene.exactCenterWorldY,
  10. viewportWidth: scene.viewportWidth,
  11. viewportHeight: scene.viewportHeight,
  12. visibleColumns: scene.visibleColumns,
  13. rotationRad: scene.rotationRad,
  14. }
  15. }
  16. export class GpsLayer implements MapLayer {
  17. projectPoint(scene: MapScene): ScreenPoint | null {
  18. if (!scene.gpsPoint) {
  19. return null
  20. }
  21. const camera = buildVectorCamera(scene)
  22. const worldPoint = calibratedLonLatToWorldTile(scene.gpsPoint, scene.zoom, scene.gpsCalibration, scene.gpsCalibrationOrigin)
  23. return worldToScreen(camera, worldPoint, false)
  24. }
  25. getPulseRadius(pulseFrame: number): number {
  26. return 18 + 6 * (0.5 + 0.5 * Math.sin(pulseFrame / 6))
  27. }
  28. draw(context: LayerRenderContext): void {
  29. const { ctx, scene, pulseFrame } = context
  30. const gpsScreenPoint = this.projectPoint(scene)
  31. if (!gpsScreenPoint) {
  32. return
  33. }
  34. const pulse = this.getPulseRadius(pulseFrame)
  35. ctx.save()
  36. ctx.beginPath()
  37. ctx.fillStyle = 'rgba(33, 158, 188, 0.22)'
  38. ctx.arc(gpsScreenPoint.x, gpsScreenPoint.y, pulse, 0, Math.PI * 2)
  39. ctx.fill()
  40. ctx.beginPath()
  41. ctx.fillStyle = '#21a1bc'
  42. ctx.arc(gpsScreenPoint.x, gpsScreenPoint.y, 9, 0, Math.PI * 2)
  43. ctx.fill()
  44. ctx.beginPath()
  45. ctx.strokeStyle = '#ffffff'
  46. ctx.lineWidth = 3
  47. ctx.arc(gpsScreenPoint.x, gpsScreenPoint.y, 13, 0, Math.PI * 2)
  48. ctx.stroke()
  49. ctx.fillStyle = '#0b3d4a'
  50. ctx.font = 'bold 16px sans-serif'
  51. ctx.textAlign = 'left'
  52. ctx.textBaseline = 'bottom'
  53. ctx.fillText('GPS', gpsScreenPoint.x + 14, gpsScreenPoint.y - 12)
  54. ctx.restore()
  55. }
  56. }