feat: AI image classification and dynamic fallback assignment
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 44s
Build & Deploy / 🧪 QA (push) Successful in 1m4s
Build & Deploy / 🏗️ Build (push) Successful in 1m54s
Build & Deploy / 🚀 Deploy (push) Successful in 28s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 43s
Build & Deploy / 🔔 Notify (push) Successful in 2s
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 44s
Build & Deploy / 🧪 QA (push) Successful in 1m4s
Build & Deploy / 🏗️ Build (push) Successful in 1m54s
Build & Deploy / 🚀 Deploy (push) Successful in 28s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 43s
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
70
lib/image-matcher.ts
Normal file
70
lib/image-matcher.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import metadataJson from '../public/assets/image-metadata.json';
|
||||
|
||||
export interface ImageMeta {
|
||||
file: string;
|
||||
category: 'pv' | 'wind' | 'fiber' | 'power' | 'battery' | 'unknown';
|
||||
environment: 'freies_feld' | 'urban' | 'wald' | 'indoor' | 'unknown';
|
||||
quality_score: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
const metadata = metadataJson as Record<string, ImageMeta>;
|
||||
|
||||
// Create a deterministic pseudo-random number based on a string
|
||||
function hashString(str: string): number {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return Math.abs(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fitting, highly-rated image for a specific project type.
|
||||
* It is deterministic based on the seed (e.g. project ID).
|
||||
*/
|
||||
export function getImageForProject(seed: string, category: string): string {
|
||||
const allImages = Object.values(metadata);
|
||||
|
||||
// 1. Filter by category
|
||||
let candidates = allImages.filter(img => img.category === category);
|
||||
|
||||
// If no candidates for category, fallback to fiber (Kabeltiefbau is safe default for ETIB)
|
||||
if (candidates.length === 0) {
|
||||
candidates = allImages.filter(img => img.category === 'fiber');
|
||||
}
|
||||
|
||||
// Still empty? Just get anything highly rated
|
||||
if (candidates.length === 0) {
|
||||
candidates = allImages;
|
||||
}
|
||||
|
||||
// 2. Sort by quality score (descending)
|
||||
candidates.sort((a, b) => b.quality_score - a.quality_score);
|
||||
|
||||
// 3. For PV and Wind, strongly prefer 'freies_feld'
|
||||
if (category === 'pv' || category === 'wind') {
|
||||
const fieldCandidates = candidates.filter(img => img.environment === 'freies_feld' && img.quality_score >= 6);
|
||||
if (fieldCandidates.length > 0) {
|
||||
candidates = fieldCandidates;
|
||||
}
|
||||
} else {
|
||||
// For others, just take top 30% quality to have some variation
|
||||
candidates = candidates.filter(img => img.quality_score >= 6);
|
||||
if (candidates.length === 0) {
|
||||
candidates = allImages.filter(img => img.category === category);
|
||||
}
|
||||
}
|
||||
|
||||
if (candidates.length === 0) {
|
||||
return '/assets/logo.png'; // Ultimate fallback
|
||||
}
|
||||
|
||||
// 4. Deterministically pick one based on the seed
|
||||
const hash = hashString(seed);
|
||||
const index = hash % candidates.length;
|
||||
|
||||
return candidates[index].file;
|
||||
}
|
||||
Reference in New Issue
Block a user