mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-23 18:03:17 +00:00
Index GPS positions with care of cardinal points
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ use Alchemy\Phrasea\Utilities\StringHelper;
|
|||||||
use Doctrine\DBAL\Connection;
|
use Doctrine\DBAL\Connection;
|
||||||
use Doctrine\DBAL\Driver\Connection as DriverConnection;
|
use Doctrine\DBAL\Driver\Connection as DriverConnection;
|
||||||
use DomainException;
|
use DomainException;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
class MetadataHydrator implements HydratorInterface
|
class MetadataHydrator implements HydratorInterface
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,8 @@ class MetadataHydrator implements HydratorInterface
|
|||||||
private $structure;
|
private $structure;
|
||||||
private $helper;
|
private $helper;
|
||||||
|
|
||||||
|
private $gps_position_buffer = [];
|
||||||
|
|
||||||
public function __construct(DriverConnection $connection, Structure $structure, RecordHelper $helper)
|
public function __construct(DriverConnection $connection, Structure $structure, RecordHelper $helper)
|
||||||
{
|
{
|
||||||
$this->connection = $connection;
|
$this->connection = $connection;
|
||||||
@@ -93,12 +96,16 @@ SQL;
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'exif':
|
case 'exif':
|
||||||
// EXIF data is single-valued
|
if (GpsPosition::isSupportedTagName($key)) {
|
||||||
|
$this->handleGpsPosition($records, $id, $key, $value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
$tag = $this->structure->getMetadataTagByName($key);
|
$tag = $this->structure->getMetadataTagByName($key);
|
||||||
if ($tag) {
|
if ($tag) {
|
||||||
$value = $this->sanitizeValue($value, $tag->getType());
|
$value = $this->sanitizeValue($value, $tag->getType());
|
||||||
}
|
}
|
||||||
$record['exif'][$key] = $value;
|
// EXIF data is single-valued
|
||||||
|
$record['metadata_tags'][$key] = $value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -106,6 +113,8 @@ SQL;
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->clearGpsPositionBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sanitizeValue($value, $type)
|
private function sanitizeValue($value, $type)
|
||||||
@@ -131,4 +140,26 @@ SQL;
|
|||||||
return $value;
|
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 = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user