Saturday, April 4, 2026 | Good afternoon!

FlyingWPress

Featured Image Data

Featured Image Data

February 16, 2025

If you use a basic WordPress install and your image optimization is not set to erase EXIF data, this might be for you.

Note: This is exclusive to the featured image. I am sure there is a way to do this by passing an attachment_id, but that was not what I needed.

I had originally created this using shortcodes and wanted to take advantage of Bricks echo functions, but I quickly realized it might be easier to add it to the dynamic data options. After prompting ChatGPT to enhance the shortcodes, it became painfully obvious that the AI wasn’t sure what to do, so I backed up and asked it to create the dynamic values and used the example from the Bricks documentation. A couple of minor tweaks, and it is working as expected.

Here is the code (use at your own risk).

UPDATE: Since my original posting, I fixed an issue with Bricks interpreting the letter M in the EXIF data as some type of math. I also added basic GPS data and a map using Open Street Maps

<?php 

function get_featured_image_data() {
$attachment_id = get_post_thumbnail_id();
if (!$attachment_id) {
return false; // No featured image found
}

$file_path = get_attached_file($attachment_id);
$exif_data = exif_read_data($file_path);
$file_info = new SplFileInfo($file_path);
$file_format = strtoupper($file_info->getExtension());
list($width, $height) = getimagesize($file_path);

$camera_model = $exif_data["Model"] ?? "Not available";

// Extract GPS Data
$gps_latitude  = isset($exif_data["GPSLatitude"]) ? convert_gps($exif_data["GPSLatitude"], $exif_data["GPSLatitudeRef"]) : "Not available";
$gps_longitude = isset($exif_data["GPSLongitude"]) ? convert_gps($exif_data["GPSLongitude"], $exif_data["GPSLongitudeRef"]) : "Not available";
$gps_altitude  = isset($exif_data["GPSAltitude"]) ? convert_altitude($exif_data["GPSAltitude"]) : "Not available";

return [
"file_size"       => size_format(filesize($file_path)),
"file_format"     => $file_format,
"image_dimensions" => $width . " x " . $height . " px",
"date_taken"      => isset($exif_data["DateTimeOriginal"])
? DateTime::createFromFormat("Y:m:d H:i:s", $exif_data["DateTimeOriginal"])
->format("F j, Y")
: "Not available",
"camera_type"     => $camera_model,
"focal_length"    => $exif_data["FocalLengthIn35mmFilm"] ?? ($exif_data["FocalLength"] ?? "Not available"),
"iso"             => $exif_data["ISOSpeedRatings"] ?? "Not available",
"f_stop"          => $exif_data["FNumber"] ?? "Not available",
"exposure_time"   => $exif_data["ExposureTime"] ?? "Not available",
"caption"         => get_the_excerpt($attachment_id),
"alt_text"        => get_post_meta($attachment_id, '_wp_attachment_image_alt', true) ?: 'No alt text',
"gps_latitude"    => $gps_latitude,
"gps_longitude"   => $gps_longitude,
"gps_altitude"    => $gps_altitude,
"gps_map"         => render_gps_map($gps_latitude, $gps_longitude),
];
}
function convert_altitude($altitude) {
// Convert altitude from meters to feet (1 meter = 3.28084 feet)
if (is_array($altitude) || strpos($altitude, "/") !== false) {
$fraction = explode("/", $altitude);
if (count($fraction) == 2 && $fraction[1] != 0) {
$altitude_value = (float) $fraction[0] / (float) $fraction[1];
} else {
$altitude_value = floatval($altitude);
}
} else {
$altitude_value = floatval($altitude);
}

$altitude_in_feet = round($altitude_value * 3.28084, 1) . " ft";
return $altitude_in_feet;
}

// ✅ Convert GPS Data (Degrees, Minutes, Seconds) to Decimal
function convert_gps($coordinate, $hemisphere) {
$degrees = count($coordinate) > 0 ? gps_to_float($coordinate[0]) : 0;
$minutes = count($coordinate) > 1 ? gps_to_float($coordinate[1]) : 0;
$seconds = count($coordinate) > 2 ? gps_to_float($coordinate[2]) : 0;

$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);

return ($hemisphere == 'S' || $hemisphere == 'W') ? -$decimal : $decimal;
}

// ✅ Helper function to convert GPS EXIF values
function gps_to_float($coord) {
$parts = explode('/', $coord);
if (count($parts) <= 0) return 0;
if (count($parts) == 1) return floatval($parts[0]);
return floatval($parts[0]) / floatval($parts[1]);
}

// ✅ Handle special case for Aperture Size separately
function img_aperture_size() {
$data = get_featured_image_data();
if ($data && isset($data["f_stop"])) {
$fraction = explode("/", $data["f_stop"]);
if (count($fraction) == 2 && $fraction[1] != 0) {
return esc_html((float)$fraction[0] / (float)$fraction[1]);
} else {
return esc_html($data["f_stop"]);
}
}
return "Not available";
}
function img_focal_length() {
$data = get_featured_image_data();

if ($data && isset($data["focal_length"])) {
$focal_length_value = $data["focal_length"];

// Ensure the value is treated as a float
if (!is_numeric($focal_length_value)) {
$focal_length_value = floatval($focal_length_value);
}

// Force output as a formatted string without unintended conversion
return number_format($focal_length_value, 1, ".", "") . " mm";
}

return "Not available";
}
add_filter('bricks/dynamic_tags_list', function ($tags) {
$custom_tags = [
['name' => '{img_alt_text}', 'label' => 'Alt Text', 'group' => 'Featured Image Data'],
['name' => '{img_aperture_size}', 'label' => 'Aperture Size', 'group' => 'Featured Image Data'],
['name' => '{img_camera_type}', 'label' => 'Camera Type', 'group' => 'Featured Image Data'],
['name' => '{img_date_taken}', 'label' => 'Date Taken', 'group' => 'Featured Image Data'],
['name' => '{img_exposure_time}', 'label' => 'Exposure Time', 'group' => 'Featured Image Data'],
['name' => '{img_file_format}', 'label' => 'File Format', 'group' => 'Featured Image Data'],
['name' => '{img_file_size}', 'label' => 'File Size', 'group' => 'Featured Image Data'],
['name' => '{img_focal_length}', 'label' => 'Focal Length', 'group' => 'Featured Image Data'],
['name' => '{img_caption}', 'label' => 'Image Caption', 'group' => 'Featured Image Data'],
['name' => '{img_dimensions}', 'label' => 'Image Dimensions', 'group' => 'Featured Image Data'],
['name' => '{img_iso}', 'label' => 'ISO', 'group' => 'Featured Image Data'],
['name' => '{img_gps_latitude}', 'label' => 'GPS Latitude', 'group' => 'Featured Image Data'],
['name' => '{img_gps_longitude}', 'label' => 'GPS Longitude', 'group' => 'Featured Image Data'],
['name' => '{img_gps_altitude}', 'label' => 'GPS Altitude', 'group' => 'Featured Image Data'],
['name' => '{img_gps_map}', 'label' => 'GPS Map', 'group' => 'Featured Image Data'], 
];

return array_merge($tags, $custom_tags);
});

add_filter('bricks/dynamic_data/render_tag', function ($tag, $post, $context) {
$data = get_featured_image_data();

switch ($tag) {
case '{img_file_size}': return $data['file_size'] ?? 'Not available';
case '{img_file_format}': return $data['file_format'] ?? 'Not available';
case '{img_dimensions}': return $data['image_dimensions'] ?? 'Not available';
case '{img_date_taken}': return $data['date_taken'] ?? 'Not available';
case '{img_camera_type}': return $data['camera_type'] ?? 'Not available';
case '{img_focal_length}': return img_focal_length();
case '{img_iso}': return $data['iso'] ?? 'Not available';
case '{img_aperture_size}': return img_aperture_size();
case '{img_exposure_time}': return $data['exposure_time'] ?? 'Not available';
case '{img_caption}': return $data['caption'] ?? 'No caption';
case '{img_alt_text}': return $data['alt_text'] ?? 'No alt text';
case '{img_gps_latitude}': return $data['gps_latitude'] ?? 'No GPS data';
case '{img_gps_longitude}': return $data['gps_longitude'] ?? 'No GPS data';
case '{img_gps_altitude}': return $data['gps_altitude'] ?? 'No GPS data';
case '{img_gps_map}': return render_gps_map($data['gps_latitude'], $data['gps_longitude']);
default: return $tag;
}
}, 20, 3);

add_filter('bricks/dynamic_data/render_content', function ($content, $post, $context) {
$data = get_featured_image_data();

$replacements = [
'{img_file_size}'     => $data['file_size'] ?? 'Not available',
'{img_file_format}'   => $data['file_format'] ?? 'Not available',
'{img_dimensions}'    => $data['image_dimensions'] ?? 'Not available',
'{img_date_taken}'    => $data['date_taken'] ?? 'Not available',
'{img_camera_type}'   => $data['camera_type'] ?? 'Not available',
'{img_focal_length}'  => img_focal_length(),
'{img_iso}'           => $data['iso'] ?? 'Not available',
'{img_aperture_size}' => img_aperture_size(),
'{img_exposure_time}' => $data['exposure_time'] ?? 'Not available',
'{img_caption}'       => $data['caption'] ?? 'No caption',
'{img_alt_text}'      => $data['alt_text'] ?? 'No alt text', 
'{img_gps_latitude}'  => $data['gps_latitude'] ?? 'No GPS data',
'{img_gps_longitude}' => $data['gps_longitude'] ?? 'No GPS data',
'{img_gps_altitude}'  => $data['gps_altitude'] ?? 'No GPS data',
'{img_gps_map}' => $data['gps_map'] ?? 'No GPS data available',
];

return str_replace(array_keys($replacements), array_values($replacements), $content);
}, 20, 3);

function render_gps_map($latitude, $longitude) {
if ($latitude === "Not available" || $longitude === "Not available") {
return "No GPS data available";
}

$latitude = esc_attr($latitude);
$longitude = esc_attr($longitude);
$zoom = 10; // Adjusted zoom level for better view

return '
<div id="osm-map" style="width: 100%; height: 300px;border-radius:var(--radius); margin-block-start:1rem;"></div>
<script>
document.addEventListener("DOMContentLoaded", function () {
var map = L.map("osm-map").setView([' . $latitude . ', ' . $longitude . '], ' . $zoom . ');

L.tileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}", {
attribution: "© Esri — Source: Esri, Maxar, Earthstar Geographics"
}).addTo(map);

L.marker([' . $latitude . ', ' . $longitude . ']).addTo(map);
});
</script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>';
}

My use case includes a new day feature photo and an image catalog website.

Screenshot of the dynamic data options.

Leave a Reply

Featured