Merge pull request #1566 from mdarse/gps-position-fix

GPS positions handling
This commit is contained in:
Benoît Burnichon
2015-11-13 11:45:41 +01:00
6 changed files with 252 additions and 6 deletions

View File

@@ -0,0 +1,99 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2014 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator;
use Assert\Assertion;
class GpsPosition
{
const LONGITUDE_TAG_NAME = 'Longitude';
const LONGITUDE_REF_TAG_NAME = 'LongitudeRef';
const LONGITUDE_REF_WEST = 'W';
const LONGITUDE_REF_EAST = 'E';
const LATITUDE_TAG_NAME = 'Latitude';
const LATITUDE_REF_TAG_NAME = 'LatitudeRef';
const LATITUDE_REF_NORTH = 'N';
const LATITUDE_REF_SOUTH = 'S';
private $longitude;
private $longitude_ref;
private $latitude;
private $latitude_ref;
public function set($tag_name, $value)
{
switch ($tag_name) {
case self::LONGITUDE_TAG_NAME:
Assertion::numeric($value);
$this->longitude = (float) $value;
break;
case self::LATITUDE_TAG_NAME:
Assertion::numeric($value);
$this->latitude = (float) $value;
break;
case self::LONGITUDE_REF_TAG_NAME:
$normalized = strtoupper($value);
if ($normalized !== self::LONGITUDE_REF_EAST && $normalized !== self::LONGITUDE_REF_WEST) {
throw new \InvalidArgumentException(sprintf('Invalid longitude reference "%s" (expecting "%s" or "%s").', $value, self::LONGITUDE_REF_EAST, self::LONGITUDE_REF_WEST));
}
$this->longitude_ref = $value;
break;
case self::LATITUDE_REF_TAG_NAME:
$normalized = strtoupper($value);
if ($normalized !== self::LATITUDE_REF_NORTH && $normalized !== self::LATITUDE_REF_SOUTH) {
throw new \InvalidArgumentException(sprintf('Invalid latitude reference "%s" (expecting "%s" or "%s").', $value, self::LATITUDE_REF_NORTH, self::LATITUDE_REF_SOUTH));
}
$this->latitude_ref = $normalized;
break;
default:
throw new \InvalidArgumentException(sprintf('Unsupported tag name "%s".', $tag_name));
}
}
public static function isSupportedTagName($tag_name)
{
return in_array($tag_name, [
self::LONGITUDE_TAG_NAME,
self::LONGITUDE_REF_TAG_NAME,
self::LATITUDE_TAG_NAME,
self::LATITUDE_REF_TAG_NAME
]);
}
public function isComplete()
{
return $this->longitude !== null
&& $this->longitude_ref !== null
&& $this->latitude !== null
&& $this->latitude_ref !== null;
}
public function getSignedLongitude()
{
if ($this->longitude === null) {
return null;
}
return $this->longitude * ($this->longitude_ref === self::LONGITUDE_REF_WEST ? -1 : 1);
}
public function getSignedLatitude()
{
if ($this->latitude === null) {
return null;
}
return $this->latitude * ($this->latitude_ref === self::LATITUDE_REF_SOUTH ? -1 : 1);
}
}

View File

@@ -19,6 +19,7 @@ use Alchemy\Phrasea\Utilities\StringHelper;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use DomainException;
use InvalidArgumentException;
class MetadataHydrator implements HydratorInterface
{
@@ -26,6 +27,8 @@ class MetadataHydrator implements HydratorInterface
private $structure;
private $helper;
private $gps_position_buffer = [];
public function __construct(DriverConnection $connection, Structure $structure, RecordHelper $helper)
{
$this->connection = $connection;
@@ -93,12 +96,16 @@ SQL;
break;
case 'exif':
// EXIF data is single-valued
if (GpsPosition::isSupportedTagName($key)) {
$this->handleGpsPosition($records, $id, $key, $value);
break;
}
$tag = $this->structure->getMetadataTagByName($key);
if ($tag) {
$value = $this->sanitizeValue($value, $tag->getType());
}
$record['exif'][$key] = $value;
// EXIF data is single-valued
$record['metadata_tags'][$key] = $value;
break;
default:
@@ -106,6 +113,8 @@ SQL;
break;
}
}
$this->clearGpsPositionBuffer();
}
private function sanitizeValue($value, $type)
@@ -131,4 +140,26 @@ SQL;
return $value;
}
}
private function handleGpsPosition(&$records, $id, $tag_name, $value)
{
// Get position object
if (!isset($this->gps_position_buffer[$id])) {
$this->gps_position_buffer[$id] = new GpsPosition();
}
$position = $this->gps_position_buffer[$id];
// Push this tag into object
$position->set($tag_name, $value);
// Try to output complete position
if ($position->isComplete()) {
$records[$id]['metadata_tags']['Longitude'] = $position->getSignedLongitude();
$records[$id]['metadata_tags']['Latitude'] = $position->getSignedLatitude();
unset($this->gps_position_buffer[$id]);
}
}
private function clearGpsPositionBuffer()
{
$this->gps_position_buffer = [];
}
}

View File

@@ -307,7 +307,7 @@ class RecordIndexer
// Thesaurus
->add('concept_path', $this->getThesaurusPathMapping())
// EXIF
->add('exif', $this->getMetadataTagMapping())
->add('metadata_tags', $this->getMetadataTagMapping())
// Status
->add('flags', $this->getFlagsMapping())
->add('flags_bitfield', 'integer')->notIndexed()

View File

@@ -39,7 +39,7 @@ class Tag implements Typed
public function getIndexField($raw = false)
{
return sprintf(
'exif.%s%s',
'metadata_tags.%s%s',
$this->name,
$raw && $this->type === Mapping::TYPE_STRING ? '.raw' : ''
);

View File

@@ -125,7 +125,9 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
const TC_DATA_MIMETYPE = 'MimeType';
const TC_DATA_FILESIZE = 'FileSize';
const TC_DATA_LONGITUDE = 'Longitude';
const TC_DATA_LONGITUDE_REF = 'LongitudeRef';
const TC_DATA_LATITUDE = 'Latitude';
const TC_DATA_LATITUDE_REF = 'LatitudeRef';
const TC_DATA_FOCALLENGTH = 'FocalLength';
const TC_DATA_CAMERAMODEL = 'CameraModel';
const TC_DATA_FLASHFIRED = 'FlashFired';
@@ -643,6 +645,10 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
self::TC_DATA_VIDEOCODEC => 'getVideoCodec',
self::TC_DATA_AUDIOCODEC => 'getAudioCodec',
self::TC_DATA_ORIENTATION => 'getOrientation',
self::TC_DATA_LONGITUDE => 'getLongitude',
self::TC_DATA_LONGITUDE_REF => 'getLongitudeRef',
self::TC_DATA_LATITUDE => 'getLatitude',
self::TC_DATA_LATITUDE_REF => 'getLatitudeRef',
];
foreach ($methods as $tc_name => $method) {
@@ -655,8 +661,6 @@ class media_subdef extends media_abstract implements cache_cacheableInterface
}
}
$datas[self::TC_DATA_LONGITUDE] = $media->getLongitude();
$datas[self::TC_DATA_LATITUDE] = $media->getLatitude();
$datas[self::TC_DATA_MIMETYPE] = $media->getFile()->getMimeType();
$datas[self::TC_DATA_FILESIZE] = $media->getFile()->getSize();

View File

@@ -0,0 +1,112 @@
<?php
namespace Alchemy\Tests\Phrasea\SearchEngine\Indexer;
use Alchemy\Phrasea\SearchEngine\Elastic\Indexer\Record\Hydrator\GpsPosition;
/**
* @group unit
* @group searchengine
* @group indexer
*/
class GpsPositionTest extends \PHPUnit_Framework_TestCase
{
// 48.856578_N_2.351828_E
public function testIsCompleteWithSingleSet()
{
$position = new GpsPosition();
$position->set('Latitude', 48.856578);
$this->assertFalse($position->isComplete());
}
public function testIsCompleteWithAllSet()
{
$position = new GpsPosition();
$position->set('Latitude', 48.856578);
$position->set('LatitudeRef', 'N');
$position->set('Longitude', 2.351828);
$position->set('LongitudeRef', 'E');
$this->assertTrue($position->isComplete());
}
/**
* @dataProvider getSupportedTagNames
*/
public function testSupportedTagNames($tag_name)
{
$this->assertTrue(GpsPosition::isSupportedTagName($tag_name));
}
public function getSupportedTagNames()
{
return [
['Longitude'],
['LongitudeRef'],
['Latitude'],
['LatitudeRef'],
[GpsPosition::LONGITUDE_TAG_NAME],
[GpsPosition::LONGITUDE_REF_TAG_NAME],
[GpsPosition::LATITUDE_TAG_NAME],
[GpsPosition::LATITUDE_REF_TAG_NAME]
];
}
/**
* @dataProvider getUnsupportedTagNames
*/
public function testUnsupportedTagNames($tag_name)
{
$this->assertFalse(GpsPosition::isSupportedTagName($tag_name));
}
public function getUnsupportedTagNames()
{
return [
['foo'],
['lOnGiTuDe']
];
}
public function testGetSignedLongitude()
{
$position = new GpsPosition();
$position->set('Longitude', 2.351828);
$this->assertEquals(2.351828, $position->getSignedLongitude());
$position = new GpsPosition();
$position->set('LongitudeRef', 'E');
$this->assertNull($position->getSignedLongitude());
$position = new GpsPosition();
$position->set('Longitude', 2.351828);
$position->set('LongitudeRef', 'E');
$this->assertEquals(2.351828, $position->getSignedLongitude());
$position = new GpsPosition();
$position->set('Longitude', 2.351828);
$position->set('LongitudeRef', 'W');
$this->assertEquals(-2.351828, $position->getSignedLongitude());
}
public function testGetSignedLatitude()
{
$position = new GpsPosition();
$position->set('Latitude', 48.856578);
$this->assertEquals(48.856578, $position->getSignedLatitude());
$position = new GpsPosition();
$position->set('LatitudeRef', 'N');
$this->assertNull($position->getSignedLatitude());
$position = new GpsPosition();
$position->set('Latitude', 48.856578);
$position->set('LatitudeRef', 'N');
$this->assertEquals(48.856578, $position->getSignedLatitude());
$position = new GpsPosition();
$position->set('Latitude', 48.856578);
$position->set('LatitudeRef', 'S');
$this->assertEquals(-48.856578, $position->getSignedLatitude());
}
}