Show individual photo markers on the map

This commit is contained in:
Eugene Burmakin 2024-11-26 16:36:02 +01:00
parent 130630b997
commit 428e927432
4 changed files with 84 additions and 33 deletions

View file

@ -64,4 +64,11 @@
justify-content: center;
background: transparent;
border: none;
border-radius: 50%;
}
.photo-marker img {
border-radius: 50%;
width: 48px;
height: 48px;
}

View file

@ -4,8 +4,32 @@ class Api::V1::PhotosController < ApiController
def index
@photos = Rails.cache.fetch("photos_#{params[:start_date]}_#{params[:end_date]}", expires_in: 1.day) do
Immich::RequestPhotos.new(current_api_user, start_date: params[:start_date], end_date: params[:end_date]).call
end
end.reject { |photo| photo['type'].downcase == 'video' }
render json: @photos, status: :ok
end
def thumbnail
response = Rails.cache.fetch("photo_thumbnail_#{params[:id]}", expires_in: 1.day) do
HTTParty.get(
"#{current_api_user.settings['immich_url']}/api/assets/#{params[:id]}/thumbnail?size=preview",
headers: {
'x-api-key' => current_api_user.settings['immich_api_key'],
'accept' => 'application/octet-stream'
}
)
end
if response.success?
send_data(
response.body,
type: 'image/jpeg',
disposition: 'inline',
status: :ok
)
else
Rails.logger.error "Failed to fetch thumbnail: #{response.code} - #{response.body}"
render json: { error: 'Failed to fetch thumbnail' }, status: response.code
end
end
end

View file

@ -782,7 +782,10 @@ export default class extends Controller {
this.layerControl = L.control.layers(this.baseMaps(), layerControl).addTo(this.map);
}
async fetchAndDisplayPhotos(startDate, endDate) {
async fetchAndDisplayPhotos(startDate, endDate, retryCount = 0) {
const MAX_RETRIES = 3;
const RETRY_DELAY = 3000; // 3 seconds
try {
const params = new URLSearchParams({
api_key: this.apiKey,
@ -796,46 +799,57 @@ export default class extends Controller {
}
const photos = await response.json();
// Clear existing photo markers
this.photoMarkers.clearLayers();
// Create markers for each photo with coordinates
photos.forEach(photo => {
if (photo.exifInfo?.latitude && photo.exifInfo?.longitude) {
const marker = L.marker([photo.exifInfo.latitude, photo.exifInfo.longitude], {
icon: L.divIcon({
className: 'photo-marker',
html: `<div class="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center">
<span class="text-white text-xs">📷</span>
</div>`,
iconSize: [24, 24]
})
});
photos.forEach(photo => this.createPhotoMarker(photo));
// Add popup with photo information
const popupContent = `
<div class="max-w-xs">
<h3 class="font-bold">${photo.originalFileName}</h3>
<p>Taken: ${new Date(photo.localDateTime).toLocaleString()}</p>
<p>Location: ${photo.exifInfo.city}, ${photo.exifInfo.state}, ${photo.exifInfo.country}</p>
${photo.type === 'VIDEO' ? '🎥 Video' : '📷 Photo'}
</div>
`;
marker.bindPopup(popupContent);
this.photoMarkers.addLayer(marker);
}
});
// Add the layer group to the map if it's not already added
if (!this.map.hasLayer(this.photoMarkers)) {
this.photoMarkers.addTo(this.map);
}
} catch (error) {
console.error('Error fetching photos:', error);
showFlashMessage('error', 'Failed to fetch photos');
if (retryCount < MAX_RETRIES) {
console.log(`Retrying in ${RETRY_DELAY/1000} seconds... (Attempt ${retryCount + 1}/${MAX_RETRIES})`);
setTimeout(() => {
this.fetchAndDisplayPhotos(startDate, endDate, retryCount + 1);
}, RETRY_DELAY);
} else {
showFlashMessage('error', 'Failed to fetch photos after multiple attempts');
}
}
}
createPhotoMarker(photo) {
if (!photo.exifInfo?.latitude || !photo.exifInfo?.longitude) return;
const thumbnailUrl = `/api/v1/photos/${photo.id}/thumbnail.jpg?api_key=${this.apiKey}`;
const icon = L.divIcon({
className: 'photo-marker',
html: `<img src="${thumbnailUrl}" style="width: 48px; height: 48px;">`,
iconSize: [48, 48]
});
const marker = L.marker(
[photo.exifInfo.latitude, photo.exifInfo.longitude],
{ icon }
);
const popupContent = `
<div class="max-w-xs">
<img src="${thumbnailUrl}"
class="w-8 h-8 mb-2 rounded"
alt="${photo.originalFileName}">
<h3 class="font-bold">${photo.originalFileName}</h3>
<p>Taken: ${new Date(photo.localDateTime).toLocaleString()}</p>
<p>Location: ${photo.exifInfo.city}, ${photo.exifInfo.state}, ${photo.exifInfo.country}</p>
${photo.type === 'VIDEO' ? '🎥 Video' : '📷 Photo'}
</div>
`;
marker.bindPopup(popupContent);
this.photoMarkers.addLayer(marker);
}
}

View file

@ -78,6 +78,12 @@ Rails.application.routes.draw do
namespace :countries do
resources :borders, only: :index
end
resources :photos do
member do
get 'thumbnail', constraints: { id: %r{[^/]+} }
end
end
end
end
end