Please bookmark this page to avoid losing your image tool!

Image City And Location Identifier

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
function processImage(originalImg, colorTheme = "#00ffcc") {
    // 1. Setup the main container
    const container = document.createElement('div');
    container.style.fontFamily = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';
    container.style.backgroundColor = '#121212';
    container.style.color = '#ffffff';
    container.style.padding = '24px';
    container.style.borderRadius = '12px';
    container.style.boxShadow = '0 8px 32px rgba(0,0,0,0.5)';
    container.style.width = '100%';
    container.style.maxWidth = '600px';
    container.style.margin = '0 auto';
    container.style.textAlign = 'center';
    container.style.boxSizing = 'border-box';

    // 2. Add Header
    const header = document.createElement('h2');
    header.innerText = 'City Location Scanner';
    header.style.margin = '0 0 20px 0';
    header.style.color = colorTheme;
    header.style.textTransform = 'uppercase';
    header.style.letterSpacing = '1px';
    container.appendChild(header);

    // 3. Setup Image & Scanner Wrapper
    const imgWrapper = document.createElement('div');
    imgWrapper.style.position = 'relative';
    imgWrapper.style.display = 'inline-block';
    imgWrapper.style.overflow = 'hidden';
    imgWrapper.style.borderRadius = '8px';
    imgWrapper.style.border = `2px solid #333`;
    imgWrapper.style.backgroundColor = '#000';
    imgWrapper.style.boxSizing = 'border-box';

    const img = new Image();
    img.crossOrigin = "Anonymous";
    img.src = originalImg.src;
    img.style.display = 'block';
    img.style.maxWidth = '100%';
    img.style.maxHeight = '350px';
    img.style.objectFit = 'contain';
    imgWrapper.appendChild(img);

    // 4. Scanner Line Overlay (simulates identification process)
    const scanLine = document.createElement('div');
    scanLine.style.position = 'absolute';
    scanLine.style.top = '0';
    scanLine.style.left = '0';
    scanLine.style.width = '100%';
    scanLine.style.height = '3px';
    scanLine.style.backgroundColor = colorTheme;
    scanLine.style.boxShadow = `0 0 15px ${colorTheme}, 0 0 5px ${colorTheme}`;
    scanLine.style.transition = 'top 0.05s linear';
    scanLine.style.zIndex = '10';
    imgWrapper.appendChild(scanLine);

    // 5. Scanner Grid Overlay
    const gridOverlay = document.createElement('div');
    gridOverlay.style.position = 'absolute';
    gridOverlay.style.top = '0';
    gridOverlay.style.left = '0';
    gridOverlay.style.width = '100%';
    gridOverlay.style.height = '100%';
    gridOverlay.style.background = `linear-gradient(rgba(0, 255, 204, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 204, 0.1) 1px, transparent 1px)`;
    gridOverlay.style.backgroundSize = '20px 20px';
    gridOverlay.style.zIndex = '5';
    imgWrapper.appendChild(gridOverlay);

    container.appendChild(imgWrapper);

    // 6. Status/Result Display Container
    const statusDiv = document.createElement('div');
    statusDiv.style.marginTop = '20px';
    statusDiv.style.padding = '15px';
    statusDiv.style.backgroundColor = '#1e1e1e';
    statusDiv.style.borderRadius = '8px';
    statusDiv.style.border = '1px solid #333';
    statusDiv.style.fontSize = '15px';
    statusDiv.style.lineHeight = '1.5';
    container.appendChild(statusDiv);

    // 7. Background Processing (Exif Extraction & Geocoding)
    (async () => {
        // Animate scanner line
        let pos = 0;
        let dir = 1;
        const scanInterval = setInterval(() => {
            pos += dir * 2;
            if (pos >= 100) { dir = -1; pos = 100; }
            if (pos <= 0) { dir = 1; pos = 0; }
            scanLine.style.top = `${pos}%`;
        }, 30);

        const updateStatus = (text) => {
            statusDiv.innerHTML = text;
        };

        const stopScanner = () => {
            clearInterval(scanInterval);
            scanLine.style.display = 'none';
            gridOverlay.style.display = 'none';
            imgWrapper.style.border = `2px solid ${colorTheme}`;
        };

        try {
            updateStatus('<div style="display:flex; justify-content:center; align-items:center; gap:10px;"><div class="spinner"></div> Extracting Exif/GPS Metadata...</div>');
            
            // Add custom spinner styles dynamically
            if (!document.getElementById('scanner-spinner-style')) {
                const style = document.createElement('style');
                style.id = 'scanner-spinner-style';
                style.innerHTML = `
                    .spinner {
                        width: 16px;
                        height: 16px;
                        border: 3px solid rgba(255,255,255,0.3);
                        border-top: 3px solid ${colorTheme};
                        border-radius: 50%;
                        animation: spin 1s linear infinite;
                    }
                    @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
                `;
                document.head.appendChild(style);
            }

            // Dynamically import Exifr to extract hidden EXIF metadata
            const exifrModule = await import('https://cdn.jsdelivr.net/npm/exifr@7.1.3/dist/full.esm.js');
            const exifr = exifrModule.default || exifrModule;
            
            // Extract GPS Data from the image
            const gpsData = await exifr.gps(originalImg.src);

            if (!gpsData) {
                stopScanner();
                imgWrapper.style.border = '2px solid #ff4444';
                updateStatus(`
                    <div style="color: #ff4444; font-weight: 600; margin-bottom: 8px;">Scan Incomplete</div>
                    <div style="color: #aaa; font-size: 13px;">No GPS/Location metadata found in this image. The city cannot be identified. Ensure the image was taken with location services enabled and hasn't been stripped of metadata.</div>
                `);
                return;
            }

            updateStatus('<div style="display:flex; justify-content:center; align-items:center; gap:10px;"><div class="spinner"></div> Location found! Identifying city...</div>');

            const { latitude, longitude } = gpsData;

            // Fetch City and Location Reverse Geocoding from free open client-side API
            const res = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${latitude}&longitude=${longitude}&localityLanguage=en`);
            const data = await res.json();

            stopScanner();

            const city = data.city || data.locality || data.principalSubdivision || "Unknown Region";
            const country = data.countryName || data.countryCode || "Unknown Country";
            const locationStr = `${city}, ${country}`;

            let htmlStr = `
                <div style="color: ${colorTheme}; font-weight: 600; font-size: 1.1em; margin-bottom: 8px;">TARGET IDENTIFIED</div>
                <div style="font-size: 1.8em; font-weight: 700; margin-bottom: 12px; display:flex; align-items:center; justify-content:center; gap:8px;">
                    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="${colorTheme}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path><circle cx="12" cy="10" r="3"></circle></svg>
                    ${locationStr}
                </div>
                <div style="font-size: 13px; color: #888; margin-bottom: 15px;">
                    Coordinates: ${latitude.toFixed(6)}, ${longitude.toFixed(6)}
                </div>
            `;

            // Embed an OSM map frame targeting the found coordinates 
            htmlStr += `
                <div style="border-radius: 8px; overflow: hidden; border: 1px solid #333;">
                    <iframe 
                        width="100%" 
                        height="200" 
                        frameborder="0" 
                        scrolling="no" 
                        marginheight="0" 
                        marginwidth="0" 
                        src="https://www.openstreetmap.org/export/embed.html?bbox=${longitude-0.015},${latitude-0.015},${longitude+0.015},${latitude+0.015}&layer=mapnik&marker=${latitude},${longitude}">
                    </iframe>
                </div>
                <div style="text-align:right; font-size:11px; margin-top:5px;">
                    <a href="https://www.openstreetmap.org/?mlat=${latitude}&mlon=${longitude}#map=15/${latitude}/${longitude}" target="_blank" style="color: ${colorTheme}; text-decoration:none;">View Maps Overlay</a>
                </div>
            `;

            updateStatus(htmlStr);

        } catch (err) {
            stopScanner();
            imgWrapper.style.border = '2px solid #ff4444';
            updateStatus(`
                <div style="color: #ff4444; font-weight: 600; margin-bottom: 8px;">Processing Error</div>
                <div style="color: #aaa; font-size: 13px;">An error occurred while identifying the image: ${err.message}</div>
            `);
        }
    })();

    return container;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image City and Location Identifier is a tool designed to extract and reveal geographical information embedded within image files. By analyzing the EXIF/GPS metadata of an uploaded image, the tool can identify the specific city, region, and country where the photo was taken. It provides the exact latitude and longitude coordinates and includes an interactive map overlay to visualize the precise location. This tool is useful for photographers wanting to verify metadata, researchers organizing location-based assets, or anyone interested in discovering the real-world origins of a photograph.

Leave a Reply

Your email address will not be published. Required fields are marked *