mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
141 lines
3.4 KiB
JavaScript
141 lines
3.4 KiB
JavaScript
|
|
/**
|
||
|
|
* Fog of war layer
|
||
|
|
* Shows explored vs unexplored areas using canvas overlay
|
||
|
|
* Does not extend BaseLayer as it uses canvas instead of MapLibre layers
|
||
|
|
*/
|
||
|
|
export class FogLayer {
|
||
|
|
constructor(map, options = {}) {
|
||
|
|
this.map = map
|
||
|
|
this.id = 'fog'
|
||
|
|
this.visible = options.visible !== undefined ? options.visible : false
|
||
|
|
this.canvas = null
|
||
|
|
this.ctx = null
|
||
|
|
this.clearRadius = options.clearRadius || 1000 // meters
|
||
|
|
this.points = []
|
||
|
|
}
|
||
|
|
|
||
|
|
add(data) {
|
||
|
|
this.points = data.features || []
|
||
|
|
this.createCanvas()
|
||
|
|
if (this.visible) {
|
||
|
|
this.show()
|
||
|
|
}
|
||
|
|
this.render()
|
||
|
|
}
|
||
|
|
|
||
|
|
update(data) {
|
||
|
|
this.points = data.features || []
|
||
|
|
this.render()
|
||
|
|
}
|
||
|
|
|
||
|
|
createCanvas() {
|
||
|
|
if (this.canvas) return
|
||
|
|
|
||
|
|
// Create canvas overlay
|
||
|
|
this.canvas = document.createElement('canvas')
|
||
|
|
this.canvas.className = 'fog-canvas'
|
||
|
|
this.canvas.style.position = 'absolute'
|
||
|
|
this.canvas.style.top = '0'
|
||
|
|
this.canvas.style.left = '0'
|
||
|
|
this.canvas.style.pointerEvents = 'none'
|
||
|
|
this.canvas.style.zIndex = '10'
|
||
|
|
this.canvas.style.display = this.visible ? 'block' : 'none'
|
||
|
|
|
||
|
|
this.ctx = this.canvas.getContext('2d')
|
||
|
|
|
||
|
|
// Add to map container
|
||
|
|
const mapContainer = this.map.getContainer()
|
||
|
|
mapContainer.appendChild(this.canvas)
|
||
|
|
|
||
|
|
// Update on map move/zoom/resize
|
||
|
|
this.map.on('move', () => this.render())
|
||
|
|
this.map.on('zoom', () => this.render())
|
||
|
|
this.map.on('resize', () => this.resizeCanvas())
|
||
|
|
|
||
|
|
this.resizeCanvas()
|
||
|
|
}
|
||
|
|
|
||
|
|
resizeCanvas() {
|
||
|
|
if (!this.canvas) return
|
||
|
|
|
||
|
|
const container = this.map.getContainer()
|
||
|
|
this.canvas.width = container.offsetWidth
|
||
|
|
this.canvas.height = container.offsetHeight
|
||
|
|
this.render()
|
||
|
|
}
|
||
|
|
|
||
|
|
render() {
|
||
|
|
if (!this.canvas || !this.ctx || !this.visible) return
|
||
|
|
|
||
|
|
const { width, height } = this.canvas
|
||
|
|
|
||
|
|
// Clear canvas
|
||
|
|
this.ctx.clearRect(0, 0, width, height)
|
||
|
|
|
||
|
|
// Draw fog overlay
|
||
|
|
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.6)'
|
||
|
|
this.ctx.fillRect(0, 0, width, height)
|
||
|
|
|
||
|
|
// Clear circles around visited points
|
||
|
|
this.ctx.globalCompositeOperation = 'destination-out'
|
||
|
|
|
||
|
|
this.points.forEach(feature => {
|
||
|
|
const coords = feature.geometry.coordinates
|
||
|
|
const point = this.map.project(coords)
|
||
|
|
|
||
|
|
// Calculate pixel radius based on zoom level
|
||
|
|
const metersPerPixel = this.getMetersPerPixel(coords[1])
|
||
|
|
const radiusPixels = this.clearRadius / metersPerPixel
|
||
|
|
|
||
|
|
this.ctx.beginPath()
|
||
|
|
this.ctx.arc(point.x, point.y, radiusPixels, 0, Math.PI * 2)
|
||
|
|
this.ctx.fill()
|
||
|
|
})
|
||
|
|
|
||
|
|
this.ctx.globalCompositeOperation = 'source-over'
|
||
|
|
}
|
||
|
|
|
||
|
|
getMetersPerPixel(latitude) {
|
||
|
|
const earthCircumference = 40075017 // meters at equator
|
||
|
|
const latitudeRadians = latitude * Math.PI / 180
|
||
|
|
const zoom = this.map.getZoom()
|
||
|
|
return earthCircumference * Math.cos(latitudeRadians) / (256 * Math.pow(2, zoom))
|
||
|
|
}
|
||
|
|
|
||
|
|
show() {
|
||
|
|
this.visible = true
|
||
|
|
if (this.canvas) {
|
||
|
|
this.canvas.style.display = 'block'
|
||
|
|
this.render()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
hide() {
|
||
|
|
this.visible = false
|
||
|
|
if (this.canvas) {
|
||
|
|
this.canvas.style.display = 'none'
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
toggle(visible = !this.visible) {
|
||
|
|
if (visible) {
|
||
|
|
this.show()
|
||
|
|
} else {
|
||
|
|
this.hide()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
remove() {
|
||
|
|
if (this.canvas) {
|
||
|
|
this.canvas.remove()
|
||
|
|
this.canvas = null
|
||
|
|
this.ctx = null
|
||
|
|
}
|
||
|
|
|
||
|
|
// Remove event listeners
|
||
|
|
this.map.off('move', this.render)
|
||
|
|
this.map.off('zoom', this.render)
|
||
|
|
this.map.off('resize', this.resizeCanvas)
|
||
|
|
}
|
||
|
|
}
|