Blog. Just Blog

Определение координат места съемки из EXIF на PHP

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Web-мастеру и не только | Автор: ManHunter
Определение координат места съемки из EXIF на PHP
Определение координат места съемки из EXIF на PHP

Окончательно решив аппаратную задачу записи GPS-координат в файлы, осталось решить программную задачу их чтения. Вариант на Ассемблере у меня уже давно есть, а вот на PHP нормальных решений не было. Причину, по которой я не пользуюсь штатными функциями PHP, я изложил в предыдущей статье, повторяться не буду. К тому же мне стало интересно самостоятельно распарсить дополнительные секции EXIF, основываясь на их спецификации.

В результате у меня получился вот такой код, который находит и разбирает EXIF-секцию в файле формата JPEG, а затем извлекает из нее GPS-координаты места съемки и до кучи количество спутников, если такая информация в файле есть.
  1. // Имя файла для обработки
  2. $file_path='DSC_1234.JPG';
  3.  
  4. // Массив с GPS-данными
  5. $gps_data=array();
  6.  
  7. $f=fopen($file_path,'r');
  8. $tmp=fread($f2);
  9. if ($tmp==chr(0xFF).chr(0xD8)) {
  10.   $section_id_stop=array(0xFFD8,0xFFDB,0xFFC4,0xFFDD,0xFFC0,0xFFDA,0xFFD9);
  11.   while (!feof($f)) {
  12.     $tmp=unpack('n',fread($f,2));
  13.     $section_id=$tmp[1];
  14.     $tmp=unpack('n',fread($f,2));
  15.     $section_length=$tmp[1];
  16.  
  17.     // Началась секция данных, заканчиваем поиск
  18.     if (in_array($section_id$section_id_stop)) {
  19.         break;
  20.     }
  21.  
  22.     // Найдена EXIF-секция
  23.     if ($section_id==0xFFE1) {
  24.       $exif=fread($f,($section_length-2));
  25.  
  26.       // Это действительно секция EXIF?
  27.       if (substr($exif,0,4)=='Exif') {
  28.         // Определить порядок следования байт
  29.         switch (substr($exif,6,2)) {
  30.           case 'MM': {
  31.             $is_motorola=true;
  32.             $mask1='n';
  33.             $mask2='N';
  34.             break;
  35.           }
  36.           case 'II': {
  37.             $is_motorola=false;
  38.             $mask1='v';
  39.             $mask2='V';
  40.             break;
  41.           }
  42.         }
  43.         // Количество тегов
  44.         $tmp=unpack($mask2,substr($exif,10,4));
  45.         $offset_tags=$tmp[1];
  46.         $tmp=unpack($mask1,substr($exif,14,2));
  47.         $num_of_tags=$tmp[1];
  48.  
  49.         if ($num_of_tags==0) { return true; }
  50.  
  51.         $offset=$offset_tags+8;
  52.  
  53.         // Поискать тег GPSInfo
  54.         for ($i=0$i<$num_of_tags$i++) {
  55.           $tmp=unpack($mask1,substr($exif,$offset,2));
  56.           $tag_id=$tmp[1];
  57.           $tmp=unpack($mask2,substr($exif,$offset+8,4));
  58.           $value=$tmp[1];
  59.  
  60.           $offset+=12;
  61.  
  62.           // GPSInfo
  63.           if ($tag_id==0x8825) {
  64.             $gps_offset=$value+6;
  65.             // Количество GPS-тегов
  66.             $tmp=unpack($mask1,substr($exif,$gps_offset,2));
  67.             $num_of_gps_tags=$tmp[1];
  68.  
  69.             $offset=$gps_offset+2;
  70.  
  71.             if ($num_of_gps_tags>0) {
  72.               // Обработка GPS-тегов
  73.               for ($i=0$i<$num_of_gps_tags$i++) {
  74.                 $tmp=unpack($mask1,substr($exif,$offset,2));
  75.                 $tag_id=$tmp[1];
  76.                 $tmp=unpack($mask2,substr($exif,$offset+8,4));
  77.                 $value=$tmp[1];
  78.  
  79.                 // GPSLatitudeRef или GPSLongitudeRef
  80.                 if ($tag_id==0x0001 || $tag_id==0x0003) {
  81.                   $tmp=unpack('V',substr($exif,$offset+8,4));
  82.                   $value=$tmp[1];
  83.                   if ($value!=0) {
  84.                     if ($tag_id==0x0001) {
  85.                       $gps_data['GPSLatitudeRef']=chr($value);
  86.                     }
  87.                     else {
  88.                       $gps_data['GPSLongitudeRef']=chr($value);
  89.                     }
  90.                   }
  91.                 }
  92.                 // GPSLatitude или GPSLongitude
  93.                 if ($tag_id==0x0002 || $tag_id==0x0004) {
  94.                   $rational_offset=$value+6;
  95.                   $tmp=unpack($mask2,substr($exif,$rational_offset+4*0,4));
  96.                   $val1=$tmp[1];
  97.                   $tmp=unpack($mask2,substr($exif,$rational_offset+4*1,4));
  98.                   $div1=$tmp[1];
  99.                   $tmp=unpack($mask2,substr($exif,$rational_offset+4*2,4));
  100.                   $val2=$tmp[1];
  101.                   $tmp=unpack($mask2,substr($exif,$rational_offset+4*3,4));
  102.                   $div2=$tmp[1];
  103.                   $tmp=unpack($mask2,substr($exif,$rational_offset+4*4,4));
  104.                   $val3=$tmp[1];
  105.                   $tmp=unpack($mask2,substr($exif,$rational_offset+4*5,4));
  106.                   $div3=$tmp[1];
  107.                   if ($div1!=&& $div2!=&& $div3!=0) {
  108.                     $tmp=round(($val1/$div1+$val2/$div2/60+$val3/$div3/3600),6);
  109.                     if ($tag_id==0x0002) {
  110.                       $gps_data['GPSLatitude']=$tmp;
  111.                     }
  112.                     else {
  113.                       $gps_data['GPSLongitude']=$tmp;
  114.                     }
  115.                   }
  116.                 }
  117.                 // GPSSatellites
  118.                 if ($tag_id==0x0008) {
  119.                   $tmp=intval(substr($exif,$offset+8,4));
  120.                   if ($tmp>0) {
  121.                     $gps_data['GPSSatellites']=$tmp;
  122.                   }
  123.                 }
  124.  
  125.                 $offset+=12;
  126.               }
  127.             }
  128.             break;
  129.           }
  130.         }
  131.       }
  132.     }
  133.     else {
  134.       // Пропустить секцию
  135.       fseek($f, ($section_length-2), SEEK_CUR);
  136.     }
  137.     // Тег GPSInfo найден
  138.     if (count($gps_data)!=0) { break; }
  139.   }
  140. }
  141. fclose($f);
  142.  
  143. // Данные GPS
  144. print_r($gps_data);
В файле значения координат хранятся в нескольких параметрах, но для удобства при обработке они автоматически пересчитываются и возвращаются уже в десятичных градусах. Используется обычная для этой операции формула:

Десятичные градусы = Градусы + Минуты/60 + Секунды/3600
Обратите внимание, что значение широты и долготы получается беззнаковым, поэтому для определения знака надо обязательно смотреть на значения GPSLatitudeRef и GPSLongitudeRef. Если направление широты равняется "S", то при установке метки на карте значение широты меняется на отрицательное, и аналогично, знак долготы меняется на минус, если направление долготы равно "W".

Приведенный в статье код можно очень легко модифицировать, чтобы извлекать не только GPS-координаты, но и любые другие данные из EXIF-секций графических файлов.

Поделиться ссылкой ВКонтакте Поделиться ссылкой на Facebook Поделиться ссылкой на LiveJournal Поделиться ссылкой в Мой Круг Добавить в Мой мир Добавить на ЛиРу (Liveinternet) Добавить в закладки Memori Добавить в закладки Google
Просмотров: 3840 | Комментариев: 1

Метки: PHP, графика, Exif
Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
ManHunter (25.06.2018 в 21:54):
Немного поправил код, благодарности loster.

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2018
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.42 сек. / MySQL: 2 (0.0054 сек.) / Память: 4.75 Mb
Наверх