macocci7/php-photo-gps

Retrieves EXIF GPS data from a photo.

1.8.3 2024-10-02 01:46 UTC

This package is auto-updated.

Last update: 2024-11-02 02:04:12 UTC


README

1. Features

PHP-PhotoGps is a simple library to get GPS data from a photo.

This library reads EXIF data of a jpeg file,

and can convert latitude/longitude into sexagesimal(English/Japanese) or decimal formats,

converts directions into degree with arrow images ,

converts speeds, date stamps and time stamps into human-readable strings.

Remote files (via http: or https:) are supported.

Supported Exif Versions:

  • 3.0
  • 2.32
  • 2.31
  • 2.3
  • 2.21
  • 2.2
  • 2.1

2. Contents

3. Requirements

  • PHP 8.1 or later

  • GD library enabled

    check with commands:

    (php -m; php -i) | grep gd
  • Exif enabled

    check with commands:

    (php -m; php -i) | grep exif
  • Composer

4. Installation

composer require macocci7/php-photo-gps

5. Usage

5.1. Usage: Basic

5.1.1. PHP

  • BasicUsage.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $filename = 'img/with_gps.jpg';    // includes GPS tags
    $pg = new PhotoGps($filename);
    
    echo "[" . $filename . "]--------------------\n";
    
    // Latitude in sexagesimal format
    echo "Latitude: " . $pg->latitudeS() . "\n";
    echo "緯度: " . $pg->lang('ja')->latitudeS() . "\n";
    
    // Longitude in sexagesimal format
    echo "Longitude: " . $pg->lang('eng')->longitudeS() . "\n";
    echo "経度: " . $pg->lang('ja')->longitudeS() . "\n";
    
    // Altitude as strings
    echo "Altitude: " . $pg->lang('eng')->altitudeS() . "\n";
    echo "高度: " . $pg->lang('ja')->altitudeS() . "\n";
    
    // Coord in decimal format ('S' and 'W' results in negative value.)
    echo "Coord: " . $pg->latitudeD() . ", " . $pg->longitudeD() . "\n";

5.1.2. Output

  • BasicUsage.txt

    [img/with_gps.jpg]--------------------
    Latitude: 37°3'26.2"N
    緯度: 北緯37度3分26.2秒
    Longitude: 140°53'32.8"E
    経度: 東経140度53分32.8秒
    Altitude: (Above Ellipsoidal Surface) 13.00 m
    高度: (正値楕円体高) 13.00 メートル
    Coord: 37.057274166667, 140.89244166667
    

5.1.3. Details

  • Import autoloader: require_once __DIR__ . '/../vendor/autoload.php'

  • use declaration: use Macocci7\PhpPhotoGps\PhotoGps;

  • Instantiation: new PhotoGps() or new PhotoGps($path)

    • Note: If HTTP or HTTPS url is specified as $path, download directory will be created in current directory, and the image will be downloaded in it.
  • Language: eng as default.

    • Get Current: lang()
    • Set as English: lang('eng')
    • Set as Japanese: lang('ja')

    Language Names are defined in config/PhotoGps.neon.

  • Latitude:

    • Decimal: latitudeD()
    • Sexagesimal: latitudeS()
  • Longitude:

    • Decimal: longitudeD()
    • Sexagesimal: longitudeS()
  • Altitude:

    • Decimal: altitude()
    • Strings with prefix & unit: altitudeS()

5.2. Usage: Format Configuration

5.2.1. Geo Data Format

for latitudeS() and longitudeS().

5.2.1.1. PHP
  • ConfigFormat.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $filename = 'img/with_gps.jpg';    // includes GPS tags
    $pg = new PhotoGps($filename);
    
    echo "[" . $filename . "]--------------------\n";
    
    // Format: default
    echo "Current format [eng]: " . $pg->lang('eng')->format() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->format() . "\n\n";
    
    // Latitude in sexagesimal format
    echo "Latitude: " . $pg->lang('eng')->latitudeS() . "\n";
    echo "緯度: " . $pg->lang('ja')->latitudeS() . "\n";
    
    // Longitude in sexagesimal format
    echo "Longitude: " . $pg->lang('eng')->longitudeS() . "\n";
    echo "経度: " . $pg->lang('ja')->longitudeS() . "\n\n";
    
    echo "[Chang format:eng]---------------------------------\n";
    
    // Configure format
    $pg->lang('eng')->format('{ref:u}: {seconds:v}{seconds:u}, {minutes:v}{minutes:u}, {degrees:v}{degrees:u}');
    
    // Current format
    echo "Current format [eng]: " . $pg->lang('eng')->format() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->format() . "\n\n";
    
    // Latitude in sexagesimal format
    echo "Latitude: " . $pg->lang('eng')->latitudeS() . "\n";
    echo "緯度: " . $pg->lang('ja')->latitudeS() . "\n";
    
    // Longitude in sexagesimal format
    echo "Longitude: " . $pg->lang('eng')->longitudeS() . "\n";
    echo "経度: " . $pg->lang('ja')->longitudeS() . "\n\n";
    
    echo "[Change format:ja]---------------------------------\n";
    
    // Configure format
    $pg->lang('ja')->format('{seconds:v}{seconds:u}, {minutes:v}{minutes:u}, {degrees:v}{degrees:u} ({ref:u})');
    
    // Current format
    echo "Current format [eng]: " . $pg->lang('eng')->format() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->format() . "\n\n";
    
    // Latitude in sexagesimal format
    echo "Latitude: " . $pg->lang('eng')->latitudeS() . "\n";
    echo "緯度: " . $pg->lang('ja')->latitudeS() . "\n";
    
    // Longitude in sexagesimal format
    echo "Longitude: " . $pg->lang('eng')->longitudeS() . "\n";
    echo "経度: " . $pg->lang('ja')->longitudeS() . "\n\n";
    
    echo "[Reset format:ja]---------------------------------\n";
    
    // Reset format
    $pg->lang('ja')->resetFormat();
    
    // Current format
    echo "Current format [eng]: " . $pg->lang('eng')->format() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->format() . "\n\n";
    
    // Latitude in sexagesimal format
    echo "Latitude: " . $pg->lang('eng')->latitudeS() . "\n";
    echo "緯度: " . $pg->lang('ja')->latitudeS() . "\n";
    
    // Longitude in sexagesimal format
    echo "Longitude: " . $pg->lang('eng')->longitudeS() . "\n";
    echo "経度: " . $pg->lang('ja')->longitudeS() . "\n\n";
5.2.1.2. Output
  • ConfigFormat.txt

    [img/with_gps.jpg]--------------------
    Current format [eng]: {degrees:v}{degrees:u}{minutes:v}{minutes:u}{seconds:v}{seconds:u}{ref:u}
    Current format [ja]: {ref:u}{degrees:v}{degrees:u}{minutes:v}{minutes:u}{seconds:v}{seconds:u}
    
    Latitude: 37°3'26.2"N
    緯度: 北緯37度3分26.2秒
    Longitude: 140°53'32.8"E
    経度: 東経140度53分32.8秒
    
    [Chang format:eng]---------------------------------
    Current format [eng]: {ref:u}: {seconds:v}{seconds:u}, {minutes:v}{minutes:u}, {degrees:v}{degrees:u}
    Current format [ja]: {ref:u}{degrees:v}{degrees:u}{minutes:v}{minutes:u}{seconds:v}{seconds:u}
    
    Latitude: N: 26.2", 3', 37°
    緯度: 北緯37度3分26.2秒
    Longitude: E: 32.8", 53', 140°
    経度: 東経140度53分32.8秒
    
    [Change format:ja]---------------------------------
    Current format [eng]: {ref:u}: {seconds:v}{seconds:u}, {minutes:v}{minutes:u}, {degrees:v}{degrees:u}
    Current format [ja]: {seconds:v}{seconds:u}, {minutes:v}{minutes:u}, {degrees:v}{degrees:u} ({ref:u})
    
    Latitude: N: 26.2", 3', 37°
    緯度: 26.2秒, 3分, 37度 (北緯)
    Longitude: E: 32.8", 53', 140°
    経度: 32.8秒, 53分, 140度 (東経)
    
    [Reset format:ja]---------------------------------
    Current format [eng]: {ref:u}: {seconds:v}{seconds:u}, {minutes:v}{minutes:u}, {degrees:v}{degrees:u}
    Current format [ja]: {ref:u}{degrees:v}{degrees:u}{minutes:v}{minutes:u}{seconds:v}{seconds:u}
    
    Latitude: N: 26.2", 3', 37°
    緯度: 北緯37度3分26.2秒
    Longitude: E: 32.8", 53', 140°
    経度: 東経140度53分32.8秒
    

5.2.1.3. Details

  • Get Format: format()

  • Set Format: format($format)

  • Reset Format: resetFormat()

  • Default Format: {ref:u}{degrees:v}{degrees:u}{minutes:v}{minutes:u}{seconds:v}{seconds:u}

  • Format-Tags:

  • latitudeS() and longitudeS() use common format.

5.2.2. Direction Data Format

for directionS(), destBearingS() and TrackS().

5.2.2.1. PHP
  • ConfigDirectionFormat.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $filename = 'img/with_gps.jpg';
    $pg = new PhotoGps($filename);
    
    echo "[" . $filename . "]--------------------\n";
    
    // Format: default
    echo "Current format [eng]: " . $pg->lang('eng')->directionFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->directionFormat() . "\n\n";
    
    // Image Direction: default format
    echo "Image Direction [eng]: " . $pg->lang('eng')->directionS() . "\n";
    echo "Image Direction [ja]: " . $pg->lang('ja')->directionS() . "\n\n";
    
    // Configure Format: eng
    $pg->lang('eng')->directionFormat('{degrees:v}{degrees:u}({ref})');
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->directionFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->directionFormat() . "\n\n";
    
    // Image Direction: Current Format
    echo "Image Direction [eng]: " . $pg->lang('eng')->directionS() . "\n";
    echo "Image Direction [ja]: " . $pg->lang('ja')->directionS() . "\n\n";
    
    // Configure Format: ja
    $pg->lang('ja')->directionFormat('{degrees:v}{degrees:u}');
    
    // Reset Format: eng
    $pg->lang('eng')->resetDirectionFormat();
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->directionFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->directionFormat() . "\n\n";
    
    // Image Direction: Current Format
    echo "Image Direction [eng]: " . $pg->lang('eng')->directionS() . "\n";
    echo "Image Direction [ja]: " . $pg->lang('ja')->directionS() . "\n\n";
5.2.2.2. Output
  • ConfigDirectionFormat.txt

    [img/with_gps.jpg]--------------------
    Current format [eng]: {ref} {degrees:v}{degrees:u}
    Current format [ja]: {ref} {degrees:v}{degrees:u}
    
    Image Direction [eng]: T 306.25°
    Image Direction [ja]: 真北 306.25度
    
    Current format [eng]: {degrees:v}{degrees:u}({ref})
    Current format [ja]: {ref} {degrees:v}{degrees:u}
    
    Image Direction [eng]: 306.25°(T)
    Image Direction [ja]: 真北 306.25度
    
    Current format [eng]: {ref} {degrees:v}{degrees:u}
    Current format [ja]: {degrees:v}{degrees:u}
    
    Image Direction [eng]: T 306.25°
    Image Direction [ja]: 306.25度
    
5.2.2.3. Details
  • Get Format: directionFormat()

  • Set Format: directionFormat($format)

  • Reset Format: resetDirectionFormat()

  • Default Format: {ref} {degrees:v}{degrees:u}

  • Format-Tags:

  • directionS(), destBearingS() and TrackS() use common format.

5.2.3. Speed Data Format

for speedS()

5.2.3.1. PHP
  • ConfigSpeedFormat.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $filename = 'img/with_gps.jpg';
    $pg = new PhotoGps($filename);
    
    echo "[" . $filename . "]--------------------\n";
    
    // Format: default
    echo "Current format [eng]: " . $pg->lang('eng')->speedFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->speedFormat() . "\n\n";
    
    // Speed: default format
    echo "Speed [eng]: " . $pg->lang('eng')->speedS() . "\n";
    echo "Speed [ja]: " . $pg->lang('ja')->speedS() . "\n\n";
    
    // Configure Format: eng
    $pg->lang('eng')->speedFormat('{speed:v}({speed:u})');
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->speedFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->speedFormat() . "\n\n";
    
    // Speed: Current Format
    echo "Speed [eng]: " . $pg->lang('eng')->speedS() . "\n";
    echo "Speed [ja]: " . $pg->lang('ja')->speedS() . "\n\n";
    
    // Configure Format: ja
    $pg->lang('ja')->speedFormat('時速{speed:v}マイル');
    
    // Reset Format: eng
    $pg->lang('eng')->resetSpeedFormat();
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->speedFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->speedFormat() . "\n\n";
    
    // Speed: Current Format
    echo "Speed [eng]: " . $pg->lang('eng')->speedS() . "\n";
    echo "Speed [ja]: " . $pg->lang('ja')->speedS() . "\n\n";
5.2.3.2. Output
  • ConfigSpeedFormat.txt

    [img/with_gps.jpg]--------------------
    Current format [eng]: {speed:v}{speed:u}
    Current format [ja]: {speed:v}{speed:u}
    
    Speed [eng]: 1.60mph
    Speed [ja]: 1.60マイル/時
    
    Current format [eng]: {speed:v}({speed:u})
    Current format [ja]: {speed:v}{speed:u}
    
    Speed [eng]: 1.60(mph)
    Speed [ja]: 1.60マイル/時
    
    Current format [eng]: {speed:v}{speed:u}
    Current format [ja]: 時速{speed:v}マイル
    
    Speed [eng]: 1.60mph
    Speed [ja]: 時速1.60マイル
    
5.2.3.3. Details
  • Get Format: speedFormat()

  • Set Format: speedFormat($format)

  • Reset Format: resetSpeedFormat()

  • Default Format: {speed:v}{speed:u}

  • Format-Tags:

5.2.4. Datestamp Data Format

for datestamp()

5.2.4.1. PHP
  • ConfigDatestampFormat.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $filename = 'img/with_gps.jpg';
    $pg = new PhotoGps($filename);
    
    echo "[" . $filename . "]--------------------\n";
    
    // Format: default
    echo "Current format [eng]: " . $pg->lang('eng')->datestampFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->datestampFormat() . "\n\n";
    
    // Datestamp: default format
    echo "Datestamp [eng]: " . $pg->lang('eng')->datestamp() . "\n";
    echo "Datestamp [ja]: " . $pg->lang('ja')->datestamp() . "\n\n";
    
    // Configure Format: eng
    $pg->lang('eng')->datestampFormat('l jS \of F Y');
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->datestampFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->datestampFormat() . "\n\n";
    
    // Datestamp: Current Format
    echo "Datestamp [eng]: " . $pg->lang('eng')->datestamp() . "\n";
    echo "Datestamp [ja]: " . $pg->lang('ja')->datestamp() . "\n\n";
    
    // Configure Format: ja
    $pg->lang('ja')->datestampFormat('n月j日(\'y)');
    
    // Reset Format: eng
    $pg->lang('eng')->resetDatestampFormat();
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->datestampFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->datestampFormat() . "\n\n";
    
    // Datestamp: Current Format
    echo "Datestamp [eng]: " . $pg->lang('eng')->datestamp() . "\n";
    echo "Datestamp [ja]: " . $pg->lang('ja')->datestamp() . "\n\n";
5.2.4.2. Output
  • ConfigDatestampFormat.txt

    [img/with_gps.jpg]--------------------
    Current format [eng]: Y/m/d
    Current format [ja]: Y年m月d日
    
    Datestamp [eng]: 2023/09/18
    Datestamp [ja]: 2023年09月18日
    
    Current format [eng]: l jS \of F Y
    Current format [ja]: Y年m月d日
    
    Datestamp [eng]: Monday 18th of September 2023
    Datestamp [ja]: 2023年09月18日
    
    Current format [eng]: Y/m/d
    Current format [ja]: n月j日('y)
    
    Datestamp [eng]: 2023/09/18
    Datestamp [ja]: 9月18日('23)
    
5.2.4.3. Details
  • Get Format: datestampFormat()
  • Set Format: datestampFormat($format)
  • Reset Format: resetDatestampFormat()
  • Default Format:
    • eng: Y/m/d
    • ja: Y年m月d日
  • Format parameter strings: See PHP Official

5.2.5. Timestamp Data Format

for timestamp()

5.2.5.1. PHP
  • ConfigTimestampFormat.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $filename = 'img/with_gps.jpg';
    $pg = new PhotoGps($filename);
    
    echo "[" . $filename . "]--------------------\n";
    
    // Format: default
    echo "Current format [eng]: " . $pg->lang('eng')->timestampFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->timestampFormat() . "\n\n";
    
    // Timestamp: default format
    echo "Timestamp [eng]: " . $pg->lang('eng')->timestamp() . "\n";
    echo "Timestamp [ja]: " . $pg->lang('ja')->timestamp() . "\n\n";
    
    // Configure Format: eng
    $pg->lang('eng')->timestampFormat('g:i a');
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->timestampFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->timestampFormat() . "\n\n";
    
    // Timestamp: Current Format
    echo "Timestamp [eng]: " . $pg->lang('eng')->timestamp() . "\n";
    echo "Timestamp [ja]: " . $pg->lang('ja')->timestamp() . "\n\n";
    
    // Configure Format: ja
    $pg->lang('ja')->timestampFormat('G時i分s秒');
    
    // Reset Format: eng
    $pg->lang('eng')->resetTimestampFormat();
    
    // Current Format
    echo "Current format [eng]: " . $pg->lang('eng')->timestampFormat() . "\n";
    echo "Current format [ja]: " . $pg->lang('ja')->timestampFormat() . "\n\n";
    
    // Timestamp: Current Format
    echo "Timestamp [eng]: " . $pg->lang('eng')->timestamp() . "\n";
    echo "Timestamp [ja]: " . $pg->lang('ja')->timestamp() . "\n\n";
5.2.5.2. Output
  • ConfigTimestampFormat.txt

    [img/with_gps.jpg]--------------------
    Current format [eng]: H:i:s
    Current format [ja]: H時i分s秒
    
    Timestamp [eng]: 03:43:32
    Timestamp [ja]: 03時43分32秒
    
    Current format [eng]: g:i a
    Current format [ja]: H時i分s秒
    
    Timestamp [eng]: 3:43 am
    Timestamp [ja]: 03時43分32秒
    
    Current format [eng]: H:i:s
    Current format [ja]: G時i分s秒
    
    Timestamp [eng]: 03:43:32
    Timestamp [ja]: 3時43分32秒
    
5.2.5.3. Details
  • Get Format: timestampFormat()
  • Set Format: timestampFormat($format)
  • Reset Format: resetTimestampFormat()
  • Default Format:
    • eng: H:i:s
    • ja: H時i分s秒
  • Format parameter strings: See PHP Official

5.3. Usage: Photo List with Checking Geo Data

Geo Data means latitude and longitude here.

The code below creates a list of photos in the dir img/.

5.3.1. PHP

  • CheckGeoData.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    
    $pg = new PhotoGps();
    
    $images = glob('img/*.{jp*g,JP*G}', GLOB_BRACE);
    sort($images);
    
    echo "# Photo List: Geo Data\n\n";
    echo "<table>\n";
    echo "<tr><th>Image</th><th>Geo</th><th>Coordinate</th></tr>\n";
    foreach ($images as $file) {
        $link = sprintf("<a href='%s'><img src='%s' width=100 /></a>", $file, $file);
        $pg->load($file);
        $hasGps = $pg->hasGps();
        $hasGeo = $pg->hasGeo();
        $hasAltitude = $pg->hasAltitude();
        echo sprintf("<td>%s</td>", $link);
        echo sprintf("<td>%s</td>", $hasGps ? 'o' : 'x');
        echo "<td>\n";
        if ($hasGeo) {
            echo sprintf(
                "<a href='%s'>%s, %s</a><br />\n",
                sprintf(
                    "https://www.google.com/maps/place/%s+%s/@%.7f,%.7f,17z/?authuser=0&entry=ttu",
                    urlencode($pg->lang('eng')->latitudeS()),
                    urlencode($pg->lang('eng')->longitudeS()),
                    $pg->latitudeD(),
                    $pg->longitudeD()
                ),
                sprintf("%.14f", $pg->latitudeD()),
                sprintf("%.14f", $pg->longitudeD())
            );
            foreach ($pg->langs() as $lang) {
                echo sprintf(
                    "%s, %s<br />\n",
                    $pg->lang($lang)->latitudeS(),
                    $pg->lang($lang)->longitudeS()
                );
            }
        } else {
            echo "No Geo Data\n\n";
        }
        if ($hasAltitude) {
            foreach ($pg->langs() as $lang) {
                echo sprintf("%s\n\n", $pg->lang($lang)->altitudeS());
            }
        } else {
            echo "No Altitude Data\n\n";
        }
        echo "</td></tr>\n";
    }
    echo "</table>\n";

5.3.2. Result

5.3.3. Details

  • Load GPS Data from a photo: load($path)
  • Check If GPS Data Exists: hasGps()
  • Check If Geo Data Exists: hasGeo()
  • Check If Altitude Exists: hasAltitude()

5.4. Usage: Read All GPS Tags

gps() method returns all Exif tags which has GPS prefix.

5.4.1. PHP

  • ReadAllGpsTags.md

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\PhotoGps;
    use Macocci7\PhpPhotoGps\Helpers\Dir;
    use Macocci7\PhpPhotoGps\Helpers\Arrow;
    use Macocci7\PhpPhotoGps\Helpers\Exif;
    
    $pg = new PhotoGps();
    $images = [
        'File with Fake GPS via HTTP' => 'http://macocci7.net/photo/gps/remote_fake_gps_001.jpg',
        'File with Fake GPS via HTTPS' => 'https://macocci7.net/photo/gps/remote_fake_gps_002.jpg',
        'Local File with GPS' => 'img/with_gps.jpg',
        'No GPS tags' => 'img/without_gps.jpg',
    ];
    $arrowSize = 30;
    
    Dir::clear('./download/');
    echo "# Exif: GPS Tags\n\n";
    
    // Loop for images
    foreach ($images as $title => $image) {
        echo "## $title\n\n";
        // Start Table
        echo "<table>\n";
        $style = 'display: flex; align-items: top;';
        echo sprintf("<tr style='%s'>\n<td>\n", $style);
    
        // Thumbnail
        echo "<img src='$image' alt='$title' width='200'>\n\n";
    
        // Load GPS Data
        $pg->load($image);
    
        // Show Attributes Converted from GPS Data
        $style = 'display: flex; justify-content: right; align-items: center;';
        $direction = $pg->direction();
        $speedS = $pg->speedS();
        $track = $pg->track();
        $destBearing = $pg->destBearing();
        $datestamp = $pg->datestamp();
        $timestamp = $pg->timestamp();
    
        echo "|Attribute|Value|\n";
        echo "|:---|---:|\n";
        echo sprintf("|ExifVersion|%s|\n", Exif::version());
    
        // Image Direction
        if (!is_null($direction)) {
            $pathArrow = sprintf('img/arrow%.2f.png', $direction);
            Arrow::make($direction)->save($pathArrow);
            echo sprintf(
                "|Image Direction|<div style='%s'><img src='%s' width=%d height=%d />%s</div>|\n",
                $style,
                $pathArrow,
                $arrowSize,
                $arrowSize,
                $pg->directionS()
            );
        }
    
        // Speed
        if (!is_null($speedS)) {
            echo sprintf("|Speed|%s|\n", $speedS);
        }
    
        // Track
        if (!is_null($track)) {
            $pathArrow = sprintf('img/arrow%.2f.png', $track);
            Arrow::make($track)->save($pathArrow);
            echo sprintf(
                "|Track|<div style='%s'><img src='%s' width=%d height=%d />%s</div>|\n",
                $style,
                $pathArrow,
                $arrowSize,
                $arrowSize,
                $pg->trackS()
            );
        }
    
        // Dest Bearing
        if (!is_null($destBearing)) {
            $pathArrow = sprintf('img/arrow%.2f.png', $destBearing);
            Arrow::make($destBearing)->save($pathArrow);
            echo sprintf(
                "|Destination Bearing|<div style='%s'><img src='%s' width=%d height=%d />%s</div>|\n",
                $style,
                $pathArrow,
                $arrowSize,
                $arrowSize,
                $pg->destBearingS()
            );
        }
    
        // Date Stamp
        echo $datestamp ? sprintf("|Datestamp|%s (UTC)|\n", $datestamp) : '';
    
        // Time Stamp
        echo $timestamp ? sprintf("|Timestamp|%s (UTC)|\n", $timestamp) : '';
    
        echo "</td>\n<td>\n\n";
    
        // Check If GPS Data Exists
        if ($pg->hasGps()) {
            // Show GPS Data
            echo "|Tag|Value|\n";
            echo "|:---|---:|\n";
            foreach ($pg->gps() as $tag => $value) {
                echo sprintf(
                    "|%s|%s|\n",
                    $tag,
                    is_array($value) ? implode('<br />', $value) : $value
                );
            }
        } else {
            echo "No GPS data.\n\n";
        }
    
        // Close Table
        echo "</td>\n</tr>\n</table>\n\n";
    }

5.4.2. Result

  • ReadAllGpsTags.md

    Exif: GPS Tags

    File with Fake GPS via HTTP

    File with Fake GPS via HTTPS

    Local File with GPS

    No GPS tags

5.4.3. Details

  • use declaration: use Macocci7\PhpPhotoGps\Helpers\Dir;

    to clear entries in download directory.

  • use declaration: use Macocci7\PhpPhotoGps\Helpers\Arrow;

    to create arrow images.

  • Clear entries in a directory: Dir::clear($dir)

  • Get Image Direction:

    • As Float: direction() returns null if not found.
    • As String: directionS() return null if not found.
  • Get Speed:

    • As Float: speed() returns null if not found.
    • As String: speedS() returns null if not found.
  • Get Track (Direction of Movement):

    • As Float: track() returns null if not found.
    • As String: trackS() returns null if not found.
  • Get Destination Bearing:

    • As Float: destBearing() returns null if not found.
    • As String: destBearingS() returns null if not found.
  • Get Date Stamp: datestamp() returns null if not found.

  • Get Time Stamp: timestamp() returns null if not found.

  • Create Arrow Image: Arrow::make($degree)->save($pathArrow)

    • $degree: must be in clockwise degrees. (0° ~ 360°)

    • Arrow:make(): returns the instance of Intervention\Image\Interfaces\ImageInterface (v3.3)

    • response() has removed since v3.

      See more: Removed Features | Intervention/image v3 Official

    • Note:

      • If the reference is 'M' (Magnetic North), the arrow image shows the relative direction from Magnetic North(Magnetic North upward).
      • Magnetic North pole moves over time. See more.
  • Note:

    • All photos in this repository were taken by macocci7 with mobile phone, and free to use.
    • Some of GPS data was manually injected with some tools.
      • GIMP v2.10.34 used for:

        • GPSLatitudeRef
        • GPSLatitude
        • GPSLongitudeRef
        • GPSLongitude
        • GPSAltitudeRef
        • GPSAltitude

        GIMP can edit only geo tags.

      • Exif Tool by Phil Harvey v12.72 used for:

        • All other GPS tags

        Exif Tool can edit all writable GPS tags.

        However, Exif Tool currently cannot set GPSSpeedRef as K (maybe a bug).

5.5. Usage: GPS Attribute Information

5.5.1. PHP

  • GpsAttrInfo.php

    <?php
    
    require_once __DIR__ . '/../vendor/autoload.php';
    
    use Macocci7\PhpPhotoGps\Helpers\Gps;
    
    //$exifVersion = "0210";
    //$exifVersion = "0220";
    //$exifVersion = "0221";
    //$exifVersion = "0230";
    //$exifVersion = "0231";
    //$exifVersion = "0232";
    $exifVersion = "0300";
    echo "# Exif" . $exifVersion . ": GPS Attribute Information\n\n";
    echo "|Field Name|Type|Count|Values|Default|Separator|\n";
    echo "|:---|:---|---:|:---|:---:|:---:|\n";
    foreach (Gps::def('exif' . $exifVersion . '.fields') as $key => $value) {
        echo sprintf(
            "|%s|%s|%d|%s|%s|%s|\n",
            $key,
            $value['type'],
            $value['count'],
            isset($value['values'])
            ? implode(
                '<br />',
                array_map(
                    fn ($k, $v) => '* ' . $k . ': ' . $v,
                    array_keys($value['values']),
                    $value['values']
                )
            )
            : '---',
            $value['default'] ?? '---',
            $value['separator'] ?? '---'
        );
    }

5.5.2. Result

5.5.3. Details

  • use declaration: use Macocci7\PhpPhotoGps\Helpers\Gps;

    to use GPS Data Interface.

  • Get GPS Tag Attributes: Gps::def() or Gps::def($tagName)

    it returns null if not found.

    GPS Tag Attributes are defined in config/Gps.neon.

    The structure of Hash array returned by Gps::def() is as below:

    • exifXXXX: XXXX is replaced with Exif Version number.
      • fields:
        • TagName: like GPSVersionID or GPSAltitude
          • type: based on Exif Standard
          • count: based on Exif Standard
          • default: based on Exif Standard
          • values: based on Exif Standard
          • separator: originaly added

    Object-like dot-separated specifiers are available for $tagName.

    For example, Gps::def('exif0300.fields.GPSLatitudeRef') returns:

    [
        'type' => 'ASCII',
        'count' => 2,
        'default' => 'None'
        'values' => [
            'N' => 'North latitude',
            'S' => 'South latitude',
        ],
    ];

6. Examples

7. LICENSE

MIT

8. Changelog

CHANGELOG

Document created: 2023/09/30

Document updated: 2024/10/02

Copyright 2023 - 2024 macocci7