Please bookmark this page to avoid losing your image tool!

Image Location Stamp Adder

(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.
async function processImage(
    originalImg,
    textColor = "white",
    fontSize = 20,
    fontFamily = "Arial",
    backgroundColor = "rgba(0, 0, 0, 0.5)",
    textBgPadding = 8, // Padding for text within its background
    stampMargin = 10, // Margin of the whole stamp from image border
    stampPosition = "bottom-left" // "top-left", "top-right", "bottom-left", "bottom-right", "center"
) {

    // Helper function to get geolocation string
    async function getGeoLocationString() {
        return new Promise((resolve) => {
            if (!navigator.geolocation) {
                resolve("Geolocation not supported");
                return;
            }

            navigator.geolocation.getCurrentPosition(
                async (position) => {
                    const lat = position.coords.latitude;
                    const lon = position.coords.longitude;
                    try {
                        // Using Nominatim (OpenStreetMap) for reverse geocoding
                        const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${lat}&lon=${lon}&accept-language=en`;
                        const response = await fetch(apiUrl);

                        if (!response.ok) {
                            resolve(`Lat: ${lat.toFixed(3)}, Lon: ${lon.toFixed(3)} (API Error ${response.status})`);
                            return;
                        }

                        const data = await response.json();

                        if (data.error) {
                            resolve(`Lat: ${lat.toFixed(3)}, Lon: ${lon.toFixed(3)} (${data.error})`);
                            return;
                        }
                        
                        let locationString = "";
                        if (data.address) {
                            const city = data.address.city || data.address.town || data.address.village;
                            const state = data.address.state || data.address.county;
                            const country = data.address.country;

                            let parts = [];
                            if (city) parts.push(city);
                            // Add state only if it's different from city (e.g. for US cities) and not too long
                            if (state && state !== city && state.length < 25) parts.push(state); 
                            if (country) parts.push(country);
                            
                            locationString = parts.join(", ");

                            // Fallback to display_name if specific parts are not found or string is too short
                            if ((!locationString || locationString.length < 5) && data.display_name) {
                                locationString = data.display_name.length > 60 ? data.display_name.substring(0, 57) + "..." : data.display_name;
                            }
                        }
                        
                        if (!locationString) { // If still no location string (e.g. middle of the ocean)
                            locationString = `Lat: ${lat.toFixed(4)}, Lon: ${lon.toFixed(4)}`;
                        }
                        resolve(locationString);

                    } catch (error) {
                        console.error("Reverse geocoding or processing error:", error);
                        resolve(`Lat: ${lat.toFixed(3)}, Lon: ${lon.toFixed(3)} (Request Error)`);
                    }
                },
                (error) => {
                    let errorMessage = "Location: ";
                    switch (error.code) {
                        case error.PERMISSION_DENIED:
                            errorMessage += "Permission Denied";
                            break;
                        case error.POSITION_UNAVAILABLE:
                            errorMessage += "Unavailable";
                            break;
                        case error.TIMEOUT:
                            errorMessage += "Timeout";
                            break;
                        default:
                            errorMessage += `Error ${error.code}`;
                            break;
                    }
                    resolve(errorMessage);
                },
                { // Geolocation options
                    enableHighAccuracy: false, // Balances accuracy and power consumption
                    timeout: 10000,         // 10 seconds to get a fix
                    maximumAge: 300000       // Use a cached position up to 5 minutes old
                }
            );
        });
    }

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // Check if the image is valid and loaded
    if (!originalImg || typeof originalImg.naturalWidth === 'undefined' || originalImg.naturalWidth === 0 || originalImg.naturalHeight === 0) {
        console.error("Image Location Stamp Adder: Invalid image provided or image not loaded.");
        // Create a small canvas with an error message
        canvas.width = 300;
        canvas.height = 100;
        ctx.fillStyle = "# сожалениюFFDDDD"; // Light red background
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "red";
        ctx.strokeStyle = "darkred";
        ctx.lineWidth = 2;
        ctx.strokeRect(0, 0, canvas.width, canvas.height);
        ctx.font = "bold 16px Arial";
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText("Error: Invalid or Broken Image", canvas.width / 2, canvas.height / 2);
        return canvas;
    }

    canvas.width = originalImg.naturalWidth;
    canvas.height = originalImg.naturalHeight;

    // Draw the original image onto the canvas
    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);

    // Get the location string (this is an async operation)
    const locationText = await getGeoLocationString();

    // Setup text properties for measurement and drawing
    ctx.font = `${fontSize}px ${fontFamily}`;
    ctx.textAlign = "left"; 
    ctx.textBaseline = "top"; // Y-coordinate refers to the top of the text's bounding box

    const textMetrics = ctx.measureText(locationText);
    const textWidth = textMetrics.width;
    // fontSize is a good approximation for the visual height of the text.
    // More precise metrics (like actualBoundingBoxAscent/Descent) could be used if needed.
    const textVisualHeight = fontSize; 

    // Calculate dimensions of the background box for the text
    const boxWidth = textWidth + 2 * textBgPadding;
    const boxHeight = textVisualHeight + 2 * textBgPadding;

    // Calculate initial X, Y coordinates for the top-left corner of the background box
    let boxX, boxY;

    switch (stampPosition) {
        case "top-left":
            boxX = stampMargin;
            boxY = stampMargin;
            break;
        case "top-right":
            boxX = canvas.width - boxWidth - stampMargin;
            boxY = stampMargin;
            break;
        case "bottom-right":
            boxX = canvas.width - boxWidth - stampMargin;
            boxY = canvas.height - boxHeight - stampMargin;
            break;
        case "center":
            boxX = (canvas.width - boxWidth) / 2;
            boxY = (canvas.height - boxHeight) / 2;
            break;
        case "bottom-left": // Default
        default:
            boxX = stampMargin;
            boxY = canvas.height - boxHeight - stampMargin;
            break;
    }
    
    // Adjust boxX, boxY to ensure the stamp (at least part of it) is visible on canvas
    // Clamp the starting point of the box to be within canvas bounds.
    // If box is wider/taller than canvas, it will start at 0,0.
    boxX = Math.max(0, Math.min(boxX, canvas.width - boxWidth));
    boxY = Math.max(0, Math.min(boxY, canvas.height - boxHeight));

    // Text drawing coordinates (absolute on canvas)
    const textDrawX = boxX + textBgPadding;
    const textDrawY = boxY + textBgPadding;

    // Draw background for the text stamp (if a color is specified)
    if (backgroundColor && backgroundColor !== "transparent" && backgroundColor !== "") {
        ctx.fillStyle = backgroundColor;
        // Draw the rectangle, ensuring it doesn't exceed canvas boundaries if stamp is larger.
        // The actual drawn width/height of the box is clipped by canvas dimensions.
        ctx.fillRect(boxX, boxY, boxWidth, boxHeight);
    }

    // Draw the location text
    ctx.fillStyle = textColor;
    // Font is already set (used for measureText).
    ctx.fillText(locationText, textDrawX, textDrawY);

    return canvas;
}

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 Location Stamp Adder is a tool that allows users to add a geolocation stamp to their images. This tool retrieves the user’s current geographical location and overlays it onto the image as text. Users can customize various aspects of the stamp, including text color, font size, font family, and background color. Additionally, they can choose the position of the stamp on the image, such as top-left, top-right, bottom-left, bottom-right, or center. This tool is ideal for photographers, travelers, and social media users who want to document and share their locations with their images.

Leave a Reply

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