dawarich/app/javascript/maps_v2/layers/photos_layer.js
2025-11-20 22:36:58 +01:00

125 lines
2.9 KiB
JavaScript

import { BaseLayer } from './base_layer'
/**
* Photos layer with thumbnail markers
* Uses circular image markers loaded from photo thumbnails
*/
export class PhotosLayer extends BaseLayer {
constructor(map, options = {}) {
super(map, { id: 'photos', ...options })
this.loadedImages = new Set()
}
async add(data) {
// Load thumbnail images before adding layer
await this.loadThumbnailImages(data)
super.add(data)
}
async update(data) {
await this.loadThumbnailImages(data)
super.update(data)
}
/**
* Load thumbnail images into map
* @param {Object} geojson - GeoJSON with photo features
*/
async loadThumbnailImages(geojson) {
if (!geojson?.features) return
const imagePromises = geojson.features.map(async (feature) => {
const photoId = feature.properties.id
const thumbnailUrl = feature.properties.thumbnail_url
const imageId = `photo-${photoId}`
// Skip if already loaded
if (this.loadedImages.has(imageId) || this.map.hasImage(imageId)) {
return
}
try {
await this.loadImageToMap(imageId, thumbnailUrl)
this.loadedImages.add(imageId)
} catch (error) {
console.warn(`Failed to load photo thumbnail ${photoId}:`, error)
}
})
await Promise.all(imagePromises)
}
/**
* Load image into MapLibre
* @param {string} imageId - Unique image identifier
* @param {string} url - Image URL
*/
async loadImageToMap(imageId, url) {
return new Promise((resolve, reject) => {
this.map.loadImage(url, (error, image) => {
if (error) {
reject(error)
return
}
// Add image if not already added
if (!this.map.hasImage(imageId)) {
this.map.addImage(imageId, image)
}
resolve()
})
})
}
getSourceConfig() {
return {
type: 'geojson',
data: this.data || {
type: 'FeatureCollection',
features: []
}
}
}
getLayerConfigs() {
return [
// Photo thumbnail background circle
{
id: `${this.id}-background`,
type: 'circle',
source: this.sourceId,
paint: {
'circle-radius': 22,
'circle-color': '#ffffff',
'circle-stroke-width': 2,
'circle-stroke-color': '#3b82f6'
}
},
// Photo thumbnail images
{
id: this.id,
type: 'symbol',
source: this.sourceId,
layout: {
'icon-image': ['concat', 'photo-', ['get', 'id']],
'icon-size': 0.15, // Scale down thumbnails
'icon-allow-overlap': true,
'icon-ignore-placement': true
}
}
]
}
getLayerIds() {
return [`${this.id}-background`, this.id]
}
/**
* Clean up loaded images when layer is removed
*/
remove() {
super.remove()
// Note: We don't remove images from map as they might be reused
}
}