Skip to content

Commit 91fcf6f

Browse files
committed
Geodata: switch to Exiv2 and read sky orientation from XMP
1 parent 4dafab0 commit 91fcf6f

3 files changed

Lines changed: 98 additions & 23 deletions

File tree

share/covise/materials/skySphere.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
<_skySphere><uniform name="tex1" type="sampler2D" value="0"/><uniform max="1.0" min="0.5" name="top" type="float" value="0.5"/><uniform max="0.0" min="0.5" name="bottom" type="float" value="0"/><uniform name="floorColor" type="vec4" value="0.0 0.0 0.0 1.0"/><vertexProgram>void main(void)
1+
<_skySphere>
2+
<uniform name="tex1" type="sampler2D" value="0"/>
3+
<uniform max="1.0" min="0.5" name="top" type="float" value="0.5"/>
4+
<uniform max="0.5" min="0.0" name="bottom" type="float" value="0.5"/>
5+
<uniform name="floorColor" type="vec4" value="0.0 0.0 0.0 1.0"/><vertexProgram>void main(void)
26
{
37
gl_TexCoord[0] = gl_MultiTexCoord0;
48
gl_Position = ftransform();
@@ -32,4 +36,4 @@ void main (void)
3236
mix(floorColor * 0.5, floorColor, smoothstep(0.0, 0.44, texcoord.y + rand(texcoord)*0.01)),
3337
texColor, smoothstep(bottom, top, texcoord.y));
3438
}
35-
</fragmentProgram></_skySphere>
39+
</fragmentProgram></_skySphere>

src/OpenCOVER/plugins/general/GeoData/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ USING(PROJ)
55
USING(CURL)
66
USING(VRML)
77

8+
covise_find_package(exiv2)
89
covise_find_package(OpenSceneGraph 3.2.0 QUIET COMPONENTS osgTerrain)
910
COVISE_FIND_PACKAGE(osgTerrain)
1011
IF(NOT OSGTERRAIN_FOUND)
@@ -27,5 +28,5 @@ set(SOURCES
2728
)
2829

2930

30-
cover_add_plugin(GeoData coCurlHTTPClient)
31+
cover_add_plugin(GeoData coCurlHTTPClient exiv2lib)
3132
target_include_directories(GeoData PRIVATE ${PROJECT_SOURCE_DIR}/../../src/3rdparty/visionaray/src/3rdparty/rapidjson/include)

src/OpenCOVER/plugins/general/GeoData/GeoDataLoader.cpp

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,11 @@
6060
#include <rapidjson/rapidjson.h>
6161
#include "cover/coVRConfig.h"
6262
#include <PluginUtil/PluginMessageTypes.h>
63-
#include <../../../../3rdparty/easyexif/exif.h>
6463
#include <stdio.h>
6564

6665
using namespace vrui;
6766
using namespace opencover;
68-
67+
#include <exiv2/exiv2.hpp>
6968
namespace opencover
7069
{
7170
namespace ui
@@ -531,8 +530,7 @@ bool GeoDataLoader::init()
531530
std::cerr << "GeoData Error: Invalid input" << std::endl;
532531
}
533532

534-
saveOffsetToConfig->setState(false);
535-
});
533+
saveOffsetToConfig->setState(false); });
536534

537535
visibilityGroup = new ui::Group(geoDataMenu, "visibility");
538536
visibilityGroup->setText("Toggle Visibility");
@@ -682,18 +680,90 @@ bool GeoDataLoader::init()
682680
fclose(fp);
683681

684682
// Parse EXIF
685-
easyexif::EXIFInfo result;
686-
int code = result.parseFrom(buf, bsize);
687-
double skyLon = result.GeoLocation.Longitude;
688-
double skyLat = result.GeoLocation.Latitude;
689-
se.skyLongitude = skyLon;
690-
se.skyLatitude = skyLat;
691-
se.skyTrueNorth = 0.0; // TODO: read true north from config if available
692-
delete[] buf;
693-
if (code)
683+
float gimbalYaw = 0.0f;
684+
float flightYaw = 0.0f;
685+
686+
auto image = Exiv2::ImageFactory::open(entry.path().string());
687+
image->readMetadata();
688+
auto &exif = image->exifData();
689+
if (exif.empty())
690+
{
691+
std::cerr << "No EXIF data found in " << entry.path().string() << std::endl;
692+
se.skyLongitude = 0.0;
693+
se.skyLatitude = 0.0;
694+
}
695+
else
696+
{
697+
auto latIt = exif.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSLatitude"));
698+
if (latIt == exif.end())
699+
{
700+
std::cerr << "No GPS Latitude found in EXIF data of " << entry.path().string() << std::endl;
701+
se.skyLatitude = 0.0;
702+
}
703+
else
704+
{
705+
Exiv2::URational latDeg = exif["Exif.GPSInfo.GPSLatitude"].toRational(0);
706+
Exiv2::URational latMin = exif["Exif.GPSInfo.GPSLatitude"].toRational(1);
707+
Exiv2::URational latSec = exif["Exif.GPSInfo.GPSLatitude"].toRational(2);
708+
se.skyLatitude = latDeg.first + latMin.first / (60.0 * latMin.second) + latSec.first / (3600.0 * latSec.second);
709+
}
710+
auto lonIt = exif.findKey(Exiv2::ExifKey("Exif.GPSInfo.GPSLongitude"));
711+
if (lonIt == exif.end())
712+
{
713+
std::cerr << "No GPS Longitude found in EXIF data of " << entry.path().string() << std::endl;
714+
se.skyLongitude = 0.0;
715+
}
716+
else
717+
{
718+
Exiv2::URational lonDeg = exif["Exif.GPSInfo.GPSLongitude"].toRational(0);
719+
Exiv2::URational lonMin = exif["Exif.GPSInfo.GPSLongitude"].toRational(1);
720+
Exiv2::URational lonSec = exif["Exif.GPSInfo.GPSLongitude"].toRational(2);
721+
se.skyLongitude = lonDeg.first + lonMin.first / (60.0 * lonMin.second) + lonSec.first / (3600.0 * lonSec.second);
722+
}
723+
724+
}
725+
726+
Exiv2::XmpData &xmpData = image->xmpData();
727+
if (xmpData.empty())
694728
{
695-
printf("Error parsing EXIF: code %d\n", code);
729+
std::cerr << "No XMP data found in " << entry.path().string() << std::endl;
730+
se.skyTrueNorth = 0.0;
731+
}
732+
else
733+
{
734+
auto gimbalYawIt = xmpData.findKey(Exiv2::XmpKey("Xmp.drone-dji.GimbalYawDegree"));
735+
if (gimbalYawIt == xmpData.end())
736+
{
737+
std::cerr << "No Gimbal Yaw found in XMP data of " << entry.path().string() << std::endl;
738+
se.skyTrueNorth = 0.0;
739+
}
740+
else
741+
{
742+
gimbalYaw = xmpData["Xmp.drone-dji.GimbalYawDegree"].toFloat();
743+
se.skyTrueNorth = gimbalYaw;
744+
if (se.skyTrueNorth < -180.0)
745+
se.skyTrueNorth += 360.0;
746+
else if (se.skyTrueNorth > 180.0)
747+
se.skyTrueNorth -= 360.0;
748+
}
749+
auto FlightYawIt = xmpData.findKey(Exiv2::XmpKey("Xmp.drone-dji.FlightYawDegree"));
750+
if (FlightYawIt == xmpData.end())
751+
{
752+
std::cerr << "No Flight Yaw found in XMP data of " << entry.path().string() << std::endl;
753+
}
754+
else
755+
{
756+
flightYaw = xmpData["Xmp.drone-dji.FlightYawDegree"].toFloat();
757+
se.skyTrueNorth = flightYaw;
758+
if (se.skyTrueNorth < -180.0)
759+
se.skyTrueNorth += 360.0;
760+
else if (se.skyTrueNorth > 180.0)
761+
se.skyTrueNorth -= 360.0;
762+
}
696763
}
764+
cout << "Exiv2: Parsed EXIF for " << entry.path().string() << ": Longitude=" << se.skyLongitude << ", Latitude=" << se.skyLatitude << endl;
765+
cout << "XMP: Parsed Gimbal Yaw for " << entry.path().string() << ": GimbalYawDegree=" << gimbalYaw << endl;
766+
cout << "XMP: Parsed Flight Yaw for " << entry.path().string() << ": FlightYawDegree=" << flightYaw << endl;
697767
}
698768
skyEntries.push_back(se);
699769
skys->append(se.name);
@@ -720,7 +790,7 @@ bool GeoDataLoader::init()
720790

721791
float farValue = coVRConfig::instance()->farClip();
722792
float scale = (farValue * 2) / 20000;
723-
skyRootNode->setMatrix(osg::Matrix::scale(scale, scale, scale) * osg::Matrix::rotate(northAngle, osg::Vec3(0, 0, 1)));
793+
skyRootNode->setMatrix(osg::Matrix::scale(scale, scale, scale) * osg::Matrix::rotate(northAngle + osg::DegreesToRadians(90.0), osg::Vec3(0, 0, -1)));
724794

725795
skyNorthSlider = new ui::Slider(skyGroup, "skyNorth");
726796
skyNorthSlider->setText("Sky True North (°)");
@@ -731,7 +801,7 @@ bool GeoDataLoader::init()
731801
northAngle = osg::DegreesToRadians(value);
732802
float farValue = coVRConfig::instance()->farClip();
733803
float scale = (farValue * 2) / 20000;
734-
skyRootNode->setMatrix(osg::Matrix::scale(scale, scale, scale)*osg::Matrix::rotate(northAngle,osg::Vec3(0,0,1))); });
804+
skyRootNode->setMatrix(osg::Matrix::scale(scale, scale, scale)*osg::Matrix::rotate(northAngle + osg::DegreesToRadians(90.0), osg::Vec3(0,0,-1))); });
735805

736806
locationGroup = new ui::Group(geoDataMenu, "locationGroup");
737807
locationGroup->setText("Location");
@@ -868,9 +938,9 @@ void GeoDataLoader::setSky(int selection)
868938
skyRootNode->addChild(sky.skyNode);
869939
}
870940
currentSkyNode = sky.skyNode;
871-
northAngle = sky.skyTrueNorth;
941+
northAngle = osg::DegreesToRadians(sky.skyTrueNorth);
872942
if (skyNorthSlider != nullptr)
873-
skyNorthSlider->setValue(osg::RadiansToDegrees(northAngle));
943+
skyNorthSlider->setValue(sky.skyTrueNorth);
874944
}
875945
}
876946

@@ -938,15 +1008,15 @@ bool GeoDataLoader::update()
9381008
// skyRootNode->setMatrix(osg::Matrix::scale(1000, 1000, 1000) * osg::Matrix::rotate(northAngle, osg::Vec3(0, 0, 1)));
9391009
float farValue = coVRConfig::instance()->farClip();
9401010
float scale = ((farValue) / 20000) + 500; // scale the sphere so that it stays a bit closer than the far clipping plane.
941-
skyRootNode->setMatrix(osg::Matrix::scale(scale, scale, scale) * osg::Matrix::rotate(northAngle, osg::Vec3(0, 0, 1)) * osg::Matrix::rotate(cover->getObjectsXform()->getMatrix().getRotate()));
1011+
skyRootNode->setMatrix(osg::Matrix::scale(scale, scale, scale) * osg::Matrix::rotate(northAngle + osg::DegreesToRadians(90.0), osg::Vec3(0, 0, -1)) * osg::Matrix::rotate(cover->getObjectsXform()->getMatrix().getRotate()));
9421012
if (editInteraction->isRunning())
9431013
{
9441014

9451015
// get the intersected node
9461016
if (cover->getIntersectedNode())
9471017
{
9481018
// position label with name
949-
//nameLabel_->setPosition(cover->getIntersectionHitPointWorld());
1019+
// nameLabel_->setPosition(cover->getIntersectionHitPointWorld());
9501020
if (cover->getIntersectedNode() != oldIntersectedNode)
9511021
{
9521022
// get the node name

0 commit comments

Comments
 (0)