diff --git a/core/libs/metadataengine/containers/template.cpp b/core/libs/metadataengine/containers/template.cpp index 0d3d1baed3..6d0542a399 100644 --- a/core/libs/metadataengine/containers/template.cpp +++ b/core/libs/metadataengine/containers/template.cpp @@ -1,225 +1,225 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-06-20 * Description : Template information container. * * Copyright (C) 2009-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "template.h" namespace Digikam { Template::Template() { } Template::~Template() { } bool Template::isNull() const { return m_templateTitle.isNull(); } bool Template::operator==(const Template& t) const { bool b1 = (m_authors == t.m_authors); bool b2 = (m_authorsPosition == t.m_authorsPosition); bool b3 = (m_credit == t.m_credit); bool b4 = (m_copyright == t.m_copyright); bool b5 = (m_rightUsageTerms == t.m_rightUsageTerms); bool b6 = (m_source == t.m_source); bool b7 = (m_instructions == t.m_instructions); bool b8 = (m_locationInfo == t.m_locationInfo); bool b9 = (m_contactInfo == t.m_contactInfo); bool b10 = (m_subjects == t.m_subjects); - /* - qCDebug(DIGIKAM_METAENGINE_LOG) << t.authors() << m_authors << b1; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.authorsPosition() << m_authorsPosition << b2; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.credit() << m_credit << b3; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.copyright() << m_copyright << b4; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.rightUsageTerms() << m_rightUsageTerms << b5; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.source() << m_source << b6; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.instructions() << m_instructions << b7; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.locationInfo() << m_locationInfo << b8; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.contactInfo() << m_contactInfo << b9; - qCDebug(DIGIKAM_METAENGINE_LOG) << t.IptcSubjects() << m_subjects << b10; - */ +/* + qCDebug(DIGIKAM_METAENGINE_LOG) << t.authors() << m_authors << b1; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.authorsPosition() << m_authorsPosition << b2; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.credit() << m_credit << b3; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.copyright() << m_copyright << b4; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.rightUsageTerms() << m_rightUsageTerms << b5; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.source() << m_source << b6; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.instructions() << m_instructions << b7; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.locationInfo() << m_locationInfo << b8; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.contactInfo() << m_contactInfo << b9; + qCDebug(DIGIKAM_METAENGINE_LOG) << t.IptcSubjects() << m_subjects << b10; +*/ return (b1 && b2 && b3 && b4 && b5 && b6 && b7 && b8 && b9 && b10); } bool Template::isEmpty() const { return ( m_authors.isEmpty() && m_authorsPosition.isEmpty() && m_credit.isEmpty() && m_copyright.isEmpty() && m_rightUsageTerms.isEmpty() && m_source.isEmpty() && m_instructions.isEmpty() && m_locationInfo.isEmpty() && m_contactInfo.isEmpty() && m_subjects.isEmpty() ); } void Template::setTemplateTitle(const QString& title) { m_templateTitle = title; } QString Template::templateTitle() const { return m_templateTitle; } void Template::setAuthors(const QStringList& authors) { m_authors = authors; m_authors.sort(); } void Template::setAuthorsPosition(const QString& authorsPosition) { m_authorsPosition = authorsPosition; } void Template::setCredit(const QString& credit) { m_credit = credit; } void Template::setCopyright(const MetaEngine::AltLangMap& copyright) { m_copyright = copyright; } void Template::setRightUsageTerms(const MetaEngine::AltLangMap& rightUsageTerms) { m_rightUsageTerms = rightUsageTerms; } void Template::setSource(const QString& source) { m_source = source; } void Template::setInstructions(const QString& instructions) { m_instructions = instructions; } void Template::setLocationInfo(const IptcCoreLocationInfo& inf) { m_locationInfo = inf; } void Template::setContactInfo(const IptcCoreContactInfo& inf) { m_contactInfo = inf; } void Template::setIptcSubjects(const QStringList& subjects) { m_subjects = subjects; m_subjects.sort(); } QStringList Template::authors() const { return m_authors; } QString Template::authorsPosition() const { return m_authorsPosition; } QString Template::credit() const { return m_credit; } MetaEngine::AltLangMap Template::copyright() const { return m_copyright; } MetaEngine::AltLangMap Template::rightUsageTerms() const { return m_rightUsageTerms; } QString Template::source() const { return m_source; } QString Template::instructions() const { return m_instructions; } IptcCoreLocationInfo Template::locationInfo() const { return m_locationInfo; } IptcCoreContactInfo Template::contactInfo() const { return m_contactInfo; } QStringList Template::IptcSubjects() const { return m_subjects; } QDebug operator<<(QDebug dbg, const Template& t) { dbg.nospace() << "Template::title: " << t.templateTitle() << ", "; dbg.nospace() << "Template::authors: " << t.authors() << ", "; dbg.nospace() << "Template::authorsPosition: " << t.authorsPosition() << ", "; dbg.nospace() << "Template::credit: " << t.credit() << ", "; dbg.nospace() << "Template::copyright: " << t.copyright() << ", "; dbg.nospace() << "Template::rightUsageTerms: " << t.rightUsageTerms() << ", "; dbg.nospace() << "Template::source: " << t.source() << ", "; dbg.nospace() << "Template::instructions: " << t.instructions() << ", "; dbg.nospace() << "Template::locationinfo: " << t.locationInfo() << ", "; dbg.nospace() << "Template::contactinfo: " << t.contactInfo(); dbg.nospace() << "Template::IptcSubjects: " << t.IptcSubjects(); return dbg.space(); } } // namespace Digikam diff --git a/core/libs/metadataengine/dmetadata/dmetadata_template.cpp b/core/libs/metadataengine/dmetadata/dmetadata_template.cpp index 045336474b..3811443704 100644 --- a/core/libs/metadataengine/dmetadata/dmetadata_template.cpp +++ b/core/libs/metadataengine/dmetadata/dmetadata_template.cpp @@ -1,288 +1,289 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-02-23 * Description : item metadata interface - template helpers. * * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * Copyright (C) 2011 by Leif Huhn * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dmetadata.h" // Qt includes #include // Local includes #include "metaenginesettings.h" #include "template.h" #include "digikam_version.h" #include "digikam_globals.h" #include "digikam_debug.h" namespace Digikam { bool DMetadata::setMetadataTemplate(const Template& t) const { if (t.isNull()) { return false; } QStringList authors = t.authors(); QString authorsPosition = t.authorsPosition(); QString credit = t.credit(); QString source = t.source(); MetaEngine::AltLangMap copyright = t.copyright(); MetaEngine::AltLangMap rightUsage = t.rightUsageTerms(); QString instructions = t.instructions(); //qCDebug(DIGIKAM_METAENGINE_LOG) << "Applying Metadata Template: " << t.templateTitle() << " :: " << authors; // Set XMP tags. XMP<->IPTC Schema from Photoshop 7.0 if (supportXmp()) { if (!setXmpTagStringSeq("Xmp.dc.creator", authors)) { return false; } if (!setXmpTagStringSeq("Xmp.tiff.Artist", authors)) { return false; } if (!setXmpTagString("Xmp.photoshop.AuthorsPosition", authorsPosition)) { return false; } if (!setXmpTagString("Xmp.photoshop.Credit", credit)) { return false; } if (!setXmpTagString("Xmp.photoshop.Source", source)) { return false; } if (!setXmpTagString("Xmp.dc.source", source)) { return false; } if (!setXmpTagStringListLangAlt("Xmp.dc.rights", copyright)) { return false; } if (!setXmpTagStringListLangAlt("Xmp.tiff.Copyright", copyright)) { return false; } if (!setXmpTagStringListLangAlt("Xmp.xmpRights.UsageTerms", rightUsage)) { return false; } if (!setXmpTagString("Xmp.photoshop.Instructions", instructions)) { return false; } } // Set IPTC tags. if (!setIptcTagsStringList("Iptc.Application2.Byline", 32, getIptcTagsStringList("Iptc.Application2.Byline"), authors)) { return false; } if (!setIptcTag(authorsPosition, 32, "Authors Title", "Iptc.Application2.BylineTitle")) { return false; } if (!setIptcTag(credit, 32, "Credit", "Iptc.Application2.Credit")) { return false; } if (!setIptcTag(source, 32, "Source", "Iptc.Application2.Source")) { return false; } if (!setIptcTag(copyright[QLatin1String("x-default")], 128, "Copyright", "Iptc.Application2.Copyright")) { return false; } if (!setIptcTag(instructions, 256, "Instructions", "Iptc.Application2.SpecialInstructions")) { return false; } if (!setIptcCoreLocation(t.locationInfo())) { return false; } if (!setCreatorContactInfo(t.contactInfo())) { return false; } if (supportXmp()) { if (!setXmpSubjects(t.IptcSubjects())) { return false; } } // Synchronize Iptc subjects tags with Xmp subjects tags. + QStringList list = t.IptcSubjects(); QStringList newList; foreach (QString str, list) // krazy:exclude=foreach { if (str.startsWith(QLatin1String("XMP"))) { str.replace(0, 3, QLatin1String("IPTC")); } newList.append(str); } if (!setIptcSubjects(getIptcSubjects(), newList)) { return false; } return true; } bool DMetadata::removeMetadataTemplate() const { // Remove Rights info. removeXmpTag("Xmp.dc.creator"); removeXmpTag("Xmp.tiff.Artist"); removeXmpTag("Xmp.photoshop.AuthorsPosition"); removeXmpTag("Xmp.photoshop.Credit"); removeXmpTag("Xmp.photoshop.Source"); removeXmpTag("Xmp.dc.source"); removeXmpTag("Xmp.dc.rights"); removeXmpTag("Xmp.tiff.Copyright"); removeXmpTag("Xmp.xmpRights.UsageTerms"); removeXmpTag("Xmp.photoshop.Instructions"); removeIptcTag("Iptc.Application2.Byline"); removeIptcTag("Iptc.Application2.BylineTitle"); removeIptcTag("Iptc.Application2.Credit"); removeIptcTag("Iptc.Application2.Source"); removeIptcTag("Iptc.Application2.Copyright"); removeIptcTag("Iptc.Application2.SpecialInstructions"); // Remove Location info. removeXmpTag("Xmp.photoshop.Country"); removeXmpTag("Xmp.iptc.CountryCode"); removeXmpTag("Xmp.photoshop.City"); removeXmpTag("Xmp.iptc.Location"); removeXmpTag("Xmp.photoshop.State"); removeIptcTag("Iptc.Application2.CountryName"); removeIptcTag("Iptc.Application2.CountryCode"); removeIptcTag("Iptc.Application2.City"); removeIptcTag("Iptc.Application2.SubLocation"); removeIptcTag("Iptc.Application2.ProvinceState"); // Remove Contact info. removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCity"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrCtry"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrExtadr"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrPcode"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiAdrRegion"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiEmailWork"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiTelWork"); removeXmpTag("Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:CiUrlWork"); // Remove IPTC Subjects. removeXmpTag("Xmp.iptc.SubjectCode"); removeIptcTag("Iptc.Application2.Subject"); return true; } Template DMetadata::getMetadataTemplate() const { Template t; getCopyrightInformation(t); t.setLocationInfo(getIptcCoreLocation()); t.setIptcSubjects(getIptcCoreSubjects()); // get from XMP or Iptc return t; } bool DMetadata::getCopyrightInformation(Template& t) const { MetadataFields fields; fields << MetadataInfo::IptcCoreCopyrightNotice << MetadataInfo::IptcCoreCreator << MetadataInfo::IptcCoreProvider << MetadataInfo::IptcCoreRightsUsageTerms << MetadataInfo::IptcCoreSource << MetadataInfo::IptcCoreCreatorJobTitle << MetadataInfo::IptcCoreInstructions; - QVariantList metadataInfos = getMetadataFields(fields); + QVariantList metadataInfos = getMetadataFields(fields); IptcCoreContactInfo contactInfo = getCreatorContactInfo(); if (!hasValidField(metadataInfos) && contactInfo.isNull()) { return false; } t.setCopyright(toAltLangMap(metadataInfos.at(0))); t.setAuthors(metadataInfos.at(1).toStringList()); t.setCredit(metadataInfos.at(2).toString()); t.setRightUsageTerms(toAltLangMap(metadataInfos.at(3))); t.setSource(metadataInfos.at(4).toString()); t.setAuthorsPosition(metadataInfos.at(5).toString()); t.setInstructions(metadataInfos.at(6).toString()); t.setContactInfo(contactInfo); return true; } } // namespace Digikam diff --git a/core/libs/metadataengine/dmetadata/dmetadata_video.cpp b/core/libs/metadataengine/dmetadata/dmetadata_video.cpp index 9f119c4a90..1d466f33c1 100644 --- a/core/libs/metadataengine/dmetadata/dmetadata_video.cpp +++ b/core/libs/metadataengine/dmetadata/dmetadata_video.cpp @@ -1,1704 +1,1764 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2018-02-26 * Description : item metadata interface - video helpers. * * References : * * FFMpeg metadata review: https://wiki.multimedia.cx/index.php/FFmpeg_Metadata * FFMpeg MP4 parser : https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mov.c#L298 * Exiv2 XMP video : https://github.com/Exiv2/exiv2/blob/master/src/properties.cpp#L1331 * Exiv2 RIFF tags : https://github.com/Exiv2/exiv2/blob/master/src/riffvideo.cpp#L83 * Apple metadata desc : https://developer.apple.com/library/content/documentation/QuickTime/QTFF/Metadata/Metadata.html * Matroska metadata desc: https://matroska.org/technical/specs/tagging/index.html * FFMpeg metadata writer: https://github.com/kritzikratzi/ofxAvCodec/blob/master/src/ofxAvUtils.cpp#L61 * * FFMpeg tags names origin: * * Generic : common tags generated by FFMpeg codecs. * RIFF files : Resource Interchange File Format tags (as AVI). * MKV files : Matroska container tags. * QT files : Quicktime container tags (Apple). * - * Copyright (C) 2020 by Gilles Caulier + * Copyright (C) 2019-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dmetadata.h" // C Ansi includes #include // Qt includes #include #include #include #include // KDE includes #include // Local includes #include "captionvalues.h" #include "digikam_debug.h" #include "digikam_config.h" #ifdef HAVE_MEDIAPLAYER // Libav includes extern "C" { #include #include #include #include } #endif namespace Digikam { -/** Search first occurrence of string in 'map' with keys given by 'lst'. - * Return the string match. - * If 'xmpTags' is not empty, register XMP tags value with string. +/** + * Search first occurrence of string in 'map' with keys given by 'lst'. + * Return the string match. + * If 'xmpTags' is not empty, register XMP tags value with string. */ QString s_setXmpTagStringFromEntry(DMetadata* const meta, const QStringList& lst, const DMetadata::MetaDataMap& map, const QStringList& xmpTags=QStringList()) { foreach (const QString& tag, lst) { DMetadata::MetaDataMap::const_iterator it = map.find(tag); if (it != map.end()) { if (meta && // Protection. !xmpTags.isEmpty()) // If xmpTags is empty, we only return the matching value from the map. { foreach (const QString& tag, xmpTags) { // Only register the tag value if it doesn't exists yet. if (meta->getXmpTagString(tag.toLatin1().data()).isNull()) { meta->setXmpTagString(tag.toLatin1().data(), it.value()); } } } return it.value(); } } return QString(); } QStringList s_keywordsSeparation(const QString& data) { QStringList keywords = data.split(QLatin1Char('/')); if (keywords.isEmpty()) { keywords = data.split(QLatin1Char(',')); if (keywords.isEmpty()) { keywords = data.split(QLatin1Char(' ')); } } return keywords; } qint64 s_secondsSinceJanuary1904(const QDateTime dt) { QDateTime dt1904(QDate(1904, 1, 1), QTime(0, 0, 0)); + return dt1904.secsTo(dt); } #ifdef HAVE_MEDIAPLAYER QString s_convertFFMpegFormatToXMP(int format) { QString data; switch (format) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: data = QLatin1String("8Int"); break; + case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: data = QLatin1String("16Int"); break; + case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: data = QLatin1String("32Int"); break; + case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP: data = QLatin1String("32Float"); break; + case AV_SAMPLE_FMT_DBL: // Not supported by XMP spec. case AV_SAMPLE_FMT_DBLP: // Not supported by XMP spec. case AV_SAMPLE_FMT_S64: // Not supported by XMP spec. case AV_SAMPLE_FMT_S64P: // Not supported by XMP spec. case AV_SAMPLE_FMT_NONE: case AV_SAMPLE_FMT_NB: default: data = QLatin1String("Other"); break; // NOTE: where are 'Compressed' and 'Packed' type from XMP spec into FFMPEG ? } return data; } DMetadata::MetaDataMap s_extractFFMpegMetadataEntriesFromDictionary(AVDictionary* const dict) { AVDictionaryEntry* entry = nullptr; DMetadata::MetaDataMap meta; do { entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX); if (entry) { QString entryValue = QString::fromUtf8(entry->value); if (QString::fromUtf8(entry->key) == QLatin1String("creation_time")) { if (QDateTime::fromString(entryValue, Qt::ISODate).toMSecsSinceEpoch() == 0) { continue; } } meta.insert(QString::fromUtf8(entry->key), entryValue); } } while (entry); return meta; } #endif bool DMetadata::loadUsingFFmpeg(const QString& filePath) { #ifdef HAVE_MEDIAPLAYER qCDebug(DIGIKAM_METAENGINE_LOG) << "Parse metadada with FFMpeg:" << filePath; #if LIBAVFORMAT_VERSION_MAJOR < 58 av_register_all(); #endif AVFormatContext* fmt_ctx = avformat_alloc_context(); int ret = avformat_open_input(&fmt_ctx, filePath.toUtf8().data(), nullptr, nullptr); if (ret < 0) { qCDebug(DIGIKAM_METAENGINE_LOG) << "avformat_open_input error: " << ret; + return false; } ret = avformat_find_stream_info(fmt_ctx, nullptr); if (ret < 0) { qCDebug(DIGIKAM_METAENGINE_LOG) << "avform_find_stream_info error: " << ret; + return false; } QString data; setXmpTagString("Xmp.video.duration", QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE))); setXmpTagString("Xmp.xmpDM.duration", QString::number((int)(1000.0 * (double)fmt_ctx->duration / (double)AV_TIME_BASE))); if (fmt_ctx->bit_rate > 0) + { setXmpTagString("Xmp.video.MaxBitRate", QString::number(fmt_ctx->bit_rate)); + } setXmpTagString("Xmp.video.StreamCount", QString::number(fmt_ctx->nb_streams)); // To only register one video, one audio stream, and one subtitle stream in XMP metadata. bool vstream = false; bool astream = false; bool sstream = false; for (uint i = 0 ; i < fmt_ctx->nb_streams ; ++i) { const AVStream* const stream = fmt_ctx->streams[i]; if (!stream) + { continue; + } AVCodecParameters* const codec = stream->codecpar; if (!codec) + { continue; + } const char* cname = avcodec_get_name(codec->codec_id); if (QLatin1String(cname) == QLatin1String("none")) { if (codec->codec_type == AVMEDIA_TYPE_AUDIO) { setXmpTagString("Xmp.audio.Codec", QString::fromUtf8(cname)); } else if (codec->codec_type == AVMEDIA_TYPE_VIDEO) { setXmpTagString("Xmp.video.Codec", QString::fromUtf8(cname)); } continue; } // ----------------------------------------- // Audio stream parsing // ----------------------------------------- if (!astream && codec->codec_type == AVMEDIA_TYPE_AUDIO) { astream = true; setXmpTagString("Xmp.audio.Codec", QString::fromUtf8(cname)); setXmpTagString("Xmp.audio.CodecDescription", QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name)); setXmpTagString("Xmp.audio.SampleRate", QString::number(codec->sample_rate)); setXmpTagString("Xmp.xmpDM.audioSampleRate", QString::number(codec->sample_rate)); // See XMP Dynamic Media properties from Adobe. // Audio Channel type is a limited untranslated string choice depending of amount of audio channels data = QString(); switch (codec->channels) { case 0: break; + case 1: data = QLatin1String("Mono"); break; + case 2: data = QLatin1String("Stereo"); break; + case 6: data = QLatin1String("5.1"); break; + case 8: data = QLatin1String("7.1"); break; + case 16: data = QLatin1String("16 Channel"); break; + default: data = QLatin1String("Other"); break; } if (!data.isEmpty()) { setXmpTagString("Xmp.audio.ChannelType", data); setXmpTagString("Xmp.xmpDM.audioChannelType", data); } setXmpTagString("Xmp.audio.Format", QString::fromUtf8(av_get_sample_fmt_name((AVSampleFormat)codec->format))); // See XMP Dynamic Media properties from Adobe. // Audio Sample type is a limited untranslated string choice depending of amount of audio samples data = s_convertFFMpegFormatToXMP(codec->format); if (!data.isEmpty()) { setXmpTagString("Xmp.audio.SampleType", data); setXmpTagString("Xmp.xmpDM.audioSampleType", data); } // -------------- MetaDataMap ameta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata); qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg audio stream metadata entries :"; qCDebug(DIGIKAM_METAENGINE_LOG) << ameta; qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------"; // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("language"), // Generic. ameta, QStringList() << QLatin1String("Xmp.audio.TrackLang")); // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("creation_time"), // Generic. ameta); if (!data.isEmpty()) { QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); setXmpTagString("Xmp.audio.TrackCreateDate", QString::number(s_secondsSinceJanuary1904(dt))); } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("handler_name"), // Generic. ameta, QStringList() << QLatin1String("Xmp.audio.HandlerDescription")); } // ----------------------------------------- // Video stream parsing // ----------------------------------------- if (!vstream && codec->codec_type == AVMEDIA_TYPE_VIDEO) { vstream = true; setXmpTagString("Xmp.video.Codec", QString::fromUtf8(cname)); setXmpTagString("Xmp.video.CodecDescription", QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name)); setXmpTagString("Xmp.video.Format", QString::fromUtf8(av_get_pix_fmt_name((AVPixelFormat)codec->format))); // Store in this tag the full description off FFMPEG video color space. + setXmpTagString("Xmp.video.ColorMode", QString::fromUtf8(av_color_space_name((AVColorSpace)codec->color_space))); VIDEOCOLORMODEL cm = VIDEOCOLORMODEL_OTHER; switch (codec->color_space) { case AVCOL_SPC_RGB: cm = VIDEOCOLORMODEL_SRGB; break; + case AVCOL_SPC_BT470BG: case AVCOL_SPC_SMPTE170M: case AVCOL_SPC_SMPTE240M: cm = VIDEOCOLORMODEL_BT601; break; + case AVCOL_SPC_BT709: cm = VIDEOCOLORMODEL_BT709; break; + case AVCOL_SPC_UNSPECIFIED: case AVCOL_SPC_RESERVED: case AVCOL_SPC_NB: cm = VIDEOCOLORMODEL_UNKNOWN; break; + default: break; } // See XMP Dynamic Media properties from Adobe. // Video Color Space is a limited untranslated string choice depending of video color space value. data = videoColorModelToString(cm); if (!data.isEmpty()) { setXmpTagString("Xmp.video.ColorSpace", data); setXmpTagString("Xmp.xmpDM.videoColorSpace", data); } // ---------- QString fo; switch (codec->field_order) { case AV_FIELD_PROGRESSIVE: fo = QLatin1String("Progressive"); break; + case AV_FIELD_TT: // Top coded first, top displayed first case AV_FIELD_BT: // Bottom coded first, top displayed first fo = QLatin1String("Upper"); break; + case AV_FIELD_BB: // Bottom coded first, bottom displayed first case AV_FIELD_TB: // Top coded first, bottom displayed first fo = QLatin1String("Lower"); break; + default: break; } if (!fo.isEmpty()) { setXmpTagString("Xmp.xmpDM.FieldOrder", fo); } // ---------- QString aspectRatio; int frameRate = -1.0; - if (codec->sample_aspect_ratio.num != 0) // Check if undefined by ffmpeg + if (codec->sample_aspect_ratio.num != 0) // Check if undefined by ffmpeg { AVRational displayAspectRatio; av_reduce(&displayAspectRatio.num, &displayAspectRatio.den, codec->width * (int64_t)codec->sample_aspect_ratio.num, codec->height * (int64_t)codec->sample_aspect_ratio.den, 1024 * 1024); aspectRatio = QString::fromLatin1("%1/%2").arg(displayAspectRatio.num) .arg(displayAspectRatio.den); } else if (codec->height) { aspectRatio = QString::fromLatin1("%1/%2").arg(codec->width) .arg(codec->height); } if (stream->avg_frame_rate.den) { frameRate = (double)stream->avg_frame_rate.num / (double)stream->avg_frame_rate.den; } setXmpTagString("Xmp.video.Width", QString::number(codec->width)); setXmpTagString("Xmp.video.FrameWidth", QString::number(codec->width)); setXmpTagString("Xmp.video.SourceImageWidth", QString::number(codec->width)); setXmpTagString("Xmp.video.Height", QString::number(codec->height)); setXmpTagString("Xmp.video.FrameHeight", QString::number(codec->height)); setXmpTagString("Xmp.video.SourceImageHeight", QString::number(codec->height)); setXmpTagString("Xmp.video.FrameSize", QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height)); setXmpTagString("Xmp.xmpDM.videoFrameSize", QString::fromLatin1("w:%1, h:%2, unit:pixels").arg(codec->width).arg(codec->height)); // Backport size in Exif and Iptc + setItemDimensions(QSize(codec->width, codec->height)); if (!aspectRatio.isEmpty()) { setXmpTagString("Xmp.video.AspectRatio", aspectRatio); setXmpTagString("Xmp.xmpDM.videoPixelAspectRatio", aspectRatio); } if (frameRate != -1.0) { setXmpTagString("Xmp.video.FrameRate", QString::number(frameRate)); // See XMP Dynamic Media properties from Adobe. // Video Color Space is a limited untranslated string choice depending of video frame rate. // https://documentation.apple.com/en/finalcutpro/usermanual/index.html#chapter=D%26section=4%26tasks=true data = QLatin1String("Other"); - if (frameRate == 24.0) + if (frameRate == 24.0) + { data = QLatin1String("24"); - else if (frameRate == 23.98 || frameRate == 29.97 || - frameRate == 30.0 || frameRate == 59.94) + } + else if ((frameRate == 23.98) || (frameRate == 29.97) || + (frameRate == 30.0) || (frameRate == 59.94)) + { data = QLatin1String("NTSC"); + } else if (frameRate == 25 || frameRate == 50) + { data = QLatin1String("PAL"); + } setXmpTagString("Xmp.xmpDM.videoFrameRate", data); } setXmpTagString("Xmp.video.BitDepth", QString::number(codec->bits_per_coded_sample)); // See XMP Dynamic Media properties from Adobe. // Video Pixel Depth is a limited untranslated string choice depending of amount of samples format. data = s_convertFFMpegFormatToXMP(codec->format); if (!data.isEmpty()) + { setXmpTagString("Xmp.xmpDM.videoPixelDepth", data); + } // ----------------------------------------- MetaDataMap vmeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata); qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg video stream metadata entries :"; qCDebug(DIGIKAM_METAENGINE_LOG) << vmeta; qCDebug(DIGIKAM_METAENGINE_LOG) << "-----------------------------------------"; // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("rotate"), // Generic. vmeta); if (!data.isEmpty()) { bool b = false; int val = data.toInt(&b); ImageOrientation ori = ORIENTATION_UNSPECIFIED; if (b) { switch (val) { case 0: ori = ORIENTATION_NORMAL; break; + case 90: ori = ORIENTATION_ROT_90; break; + case 180: ori = ORIENTATION_ROT_180; break; + case 270: ori = ORIENTATION_ROT_270; break; + default: break; } setXmpTagString("Xmp.video.Orientation", QString::number(ori)); + // Backport orientation in Exif + setItemOrientation(ori); } } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("language") // Generic. << QLatin1String("ILNG") // RIFF files. << QLatin1String("LANG"), // RIFF files. vmeta, QStringList() << QLatin1String("Xmp.video.Language")); // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("creation_time") // Generic. << QLatin1String("_STATISTICS_WRITING_DATE_UTC"), // MKV files. vmeta); if (!data.isEmpty()) { QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); setXmpTagString("Xmp.video.TrackCreateDate", QString::number(s_secondsSinceJanuary1904(dt))); setXmpTagString("Xmp.xmpDM.shotDate", dt.toString()); } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("handler_name"), // Generic. vmeta, QStringList() << QLatin1String("Xmp.video.HandlerDescription")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("TVER") // RIFF files. << QLatin1String("_STATISTICS_WRITING_APP"), // MKV files. vmeta, QStringList() << QLatin1String("Xmp.video.SoftwareVersion")); } // ----------------------------------------- // Subtitle stream parsing // ----------------------------------------- if (!sstream && codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { sstream = true; setXmpTagString("Xmp.video.SubTCodec", QString::fromUtf8(cname)); setXmpTagString("Xmp.video.SubTCodecInfo", QString::fromUtf8(avcodec_descriptor_get_by_name(cname)->long_name)); // ----------------------------------------- MetaDataMap smeta = s_extractFFMpegMetadataEntriesFromDictionary(stream->metadata); qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg subtitle stream metadata entries :"; qCDebug(DIGIKAM_METAENGINE_LOG) << smeta; qCDebug(DIGIKAM_METAENGINE_LOG) << "--------------------------------------------"; // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("subtitle") // Generic. << QLatin1String("title"), // Generic. smeta, QStringList() << QLatin1String("Xmp.video.Subtitle")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("language"), // Generic. smeta, QStringList() << QLatin1String("Xmp.video.SubTLang")); } } // ----------------------------------------- // Root container parsing // ----------------------------------------- MetaDataMap rmeta = s_extractFFMpegMetadataEntriesFromDictionary(fmt_ctx->metadata); qCDebug(DIGIKAM_METAENGINE_LOG) << "-- FFMpeg root container metadata entries :"; qCDebug(DIGIKAM_METAENGINE_LOG) << rmeta; qCDebug(DIGIKAM_METAENGINE_LOG) << "------------------------------------------"; // ---------------------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("major_brand"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.MajorBrand")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("compatible_brands"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.CompatibleBrands")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("minor_version"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.MinorVersion")); // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("keywords") // Generic. << QLatin1String("IMIT") // RIFF files. << QLatin1String("KEYWORDS") // MKV files. << QLatin1String("com.apple.quicktime.keywords"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.InfoText")); if (!data.isEmpty()) { QStringList keywords = s_keywordsSeparation(data); if (!keywords.isEmpty()) { setXmpKeywords(keywords); setIptcKeywords(QStringList(), keywords); } } // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("category") // Generic. << QLatin1String("ISBJ") // RIFF files. << QLatin1String("SUBJECT"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.Subject")); if (!data.isEmpty()) { QStringList categories = s_keywordsSeparation(data); if (!categories.isEmpty()) { setXmpSubCategories(categories); setIptcSubCategories(QStringList(), categories); } } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("premiere_version") // Generic. << QLatin1String("quicktime_version") // Generic. << QLatin1String("ISFT") // Riff files << QLatin1String("com.apple.quicktime.software"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.SoftwareVersion")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("firmware") // Generic. << QLatin1String("com.apple.proapps.serialno"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.FirmwareVersion")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("composer") // Generic. << QLatin1String("COMPOSER"), // MKV files rmeta, QStringList() << QLatin1String("Xmp.video.Composer") << QLatin1String("Xmp.xmpDM.composer")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("com.apple.quicktime.displayname"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Name")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("playback_requirements"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.Requirements")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("lyrics"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.Lyrics")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("filename"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.FileName")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("disk"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.xmpDM.discNumber")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("performers"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.Performers")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("producer") // Generic. << QLatin1String("PRODUCER") // MKV files. << QLatin1String("com.apple.quicktime.producer"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Producer")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("artist") // Generic. << QLatin1String("album_artist") // Generic. << QLatin1String("original_artist") // Generic. << QLatin1String("com.apple.quicktime.artist") // QT files. << QLatin1String("IART") // RIFF files. << QLatin1String("ARTIST") // MKV files. << QLatin1String("author") // Generic. << QLatin1String("com.apple.quicktime.author"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Artist") << QLatin1String("Xmp.xmpDM.artist")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("director") // Generic. << QLatin1String("DIRC") // RIFF files. << QLatin1String("DIRECTOR") // MKV files. << QLatin1String("com.apple.quicktime.director"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Director") << QLatin1String("Xmp.xmpDM.director")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("media_type") // Generic. << QLatin1String("IMED") // RIFF files. << QLatin1String("ORIGINAL_MEDIA_TYPE"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.Medium")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("grouping"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.Grouping")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("BPS"), // MKV files rmeta, QStringList() << QLatin1String("Xmp.video.MaxBitRate")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ISRC"), // MKV files rmeta, QStringList() << QLatin1String("Xmp.video.ISRCCode")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("CONTENT_TYPE"), // MKV files rmeta, QStringList() << QLatin1String("Xmp.video.ExtendedContentDescription")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("FPS"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.videoFrameRate") << QLatin1String("Xmp.xmpDM.FrameRate")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("encoder") // Generic. << QLatin1String("ENCODER"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.Encoder")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("com.apple.proapps.clipID"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.FileID")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("original_source") // Generic. << QLatin1String("ISRC") // Riff files << QLatin1String("com.apple.proapps.cameraName"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Source")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("original_format") // Generic. << QLatin1String("com.apple.proapps.originalFormat"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Format")); // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("rating") // Generic. << QLatin1String("IRTD") // RIFF files. << QLatin1String("RATE") // RIFF files. << QLatin1String("RATING") // MKV files. << QLatin1String("com.apple.quicktime.rating.user"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Rating") << QLatin1String("Xmp.video.Rate")); if (!data.isEmpty()) { // Backport rating in Exif and Iptc + bool b = false; int rating = data.toInt(&b); if (b) { setItemRating(rating); } } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("make") // Generic. << QLatin1String("com.apple.quicktime.make"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Make")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("model") // Generic. << QLatin1String("com.apple.quicktime.model"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Model") << QLatin1String("Xmp.xmpDM.cameraModel")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("URL") // Generic. << QLatin1String("TURL"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.URL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("title") // Generic. << QLatin1String("INAM") // RIFF files. << QLatin1String("TITL") // RIFF files. << QLatin1String("TITLE") // MKV files. << QLatin1String("com.apple.quicktime.title"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Title") << QLatin1String("Xmp.xmpDM.shotName")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("copyright") // Generic. << QLatin1String("ICOP") // RIFF files. << QLatin1String("COPYRIGHT") // MKV files. << QLatin1String("com.apple.quicktime.copyright"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Copyright") << QLatin1String("Xmp.xmpDM.copyright")); // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("comment") // Generic. << QLatin1String("description") // Generic. << QLatin1String("CMNT") // Riff Files. << QLatin1String("COMN") // Riff Files. << QLatin1String("ICMT") // Riff Files. << QLatin1String("COMMENT") // MKV Files. << QLatin1String("DESCRIPTION") // MKV Files. << QLatin1String("com.apple.quicktime.description"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Comment") << QLatin1String("Xmp.xmpDM.logComment")); if (!data.isEmpty()) { // Backport comment in Exif and Iptc CaptionsMap capMap; MetaEngine::AltLangMap comMap; comMap.insert(QLatin1String("x-default"), data); capMap.setData(comMap, MetaEngine::AltLangMap(), QString(), MetaEngine::AltLangMap()); setItemComments(capMap); } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("synopsis") // Generic. << QLatin1String("SUMMARY") // MKV files. << QLatin1String("SYNOPSIS"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.Information")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("lyrics") // Generic. << QLatin1String("LYRICS"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.xmpDM.lyrics")); // -------------- for (int i = 1 ; i <= 9 ; ++i) { s_setXmpTagStringFromEntry(this, - QStringList() << QString::fromLatin1("IAS%1").arg(i), // RIFF files. - rmeta, - QStringList() << QString::fromLatin1("Xmp.video.Edit%1").arg(i)); + QStringList() << QString::fromLatin1("IAS%1").arg(i), // RIFF files. + rmeta, + QStringList() << QString::fromLatin1("Xmp.video.Edit%1").arg(i)); } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("encoded_by") // Generic. << QLatin1String("CODE") // RIFF files. << QLatin1String("IECN") // RIFF files. << QLatin1String("ENCODED_BY"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.EncodedBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("DISP"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.SchemeTitle")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("AGES") // RIFF files. << QLatin1String("ICRA") // MKV files. << QLatin1String("LAW_RATING"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.Rated")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IBSU"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.BaseURL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICAS"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.DefaultStream")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICDS"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.CostumeDesigner")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICMS"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Commissioned")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICNM"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Cinematographer")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICNT"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Country")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IARL"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.ArchivalLocation")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICRP"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Cropped")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IDIM"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Dimensions")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IDPI"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.DotsPerInch")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IDST") // RIFF files. << QLatin1String("DISTRIBUTED_BY"), // MKV files. rmeta, QStringList() << QLatin1String("Xmp.video.DistributedBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IEDT"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.EditedBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IENG"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Engineer") << QLatin1String("Xmp.xmpDM.engineer")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IKEY"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.PerformerKeywords")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ILGT"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Lightness")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ILGU"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.LogoURL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ILIU"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.LogoIconURL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IMBI"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.InfoBannerImage")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IMBU"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.InfoBannerURL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IMIU"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.InfoURL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IMUS"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.MusicBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IPDS"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.ProductionDesigner")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IPLT"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.NumOfColors")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IPRD"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Product")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IPRO"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.ProducedBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IRIP"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.RippedBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ISGN"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.SecondaryGenre")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ISHP"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Sharpness")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ISRF"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.SourceForm")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ISTD"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.ProductionStudio")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ISTR") // RIFF files. << QLatin1String("STAR"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Starring")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ITCH"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Technician")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IWMU"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.WatermarkURL")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("IWRI"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.WrittenBy")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("PRT1"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Part")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("PRT2"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.NumOfParts")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("STAT"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Statistics")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("TAPE"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.TapeName")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("TCDO"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.EndTimecode")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("TCOD"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.StartTimecode")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("TLEN"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Length")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("TORG"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.Organization")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("VMAJ"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.VegasVersionMajor")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("VMIN"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.VegasVersionMinor")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("LOCA"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.LocationInfo") << QLatin1String("Xmp.xmpDM.shotLocation")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("album") // Generic. << QLatin1String("com.apple.quicktime.album"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Album") << QLatin1String("Xmp.xmpDM.album")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("genre") // Generic. << QLatin1String("GENR") // RIFF files. << QLatin1String("IGNR") // RIFF files. << QLatin1String("GENRE") // MKV files. << QLatin1String("com.apple.quicktime.genre"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.Genre") << QLatin1String("Xmp.xmpDM.genre")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("track") // Generic. << QLatin1String("TRCK"), // RIFF files. rmeta, QStringList() << QLatin1String("Xmp.video.TrackNumber") << QLatin1String("Xmp.xmpDM.trackNumber")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("year") // Generic. << QLatin1String("YEAR") // RIFF files. << QLatin1String("com.apple.quicktime.year"), rmeta, QStringList() << QLatin1String("Xmp.video.Year")); // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("ICRD") // Riff files << QLatin1String("DATE_DIGITIZED"), // MKV files rmeta, QStringList() << QLatin1String("Xmp.video.DateTimeDigitized")); // -------------- QStringList videoDateTimeOriginal; videoDateTimeOriginal << QLatin1String("creation_time") // Generic. << QLatin1String("DTIM") // RIFF files. << QLatin1String("DATE_RECORDED") // MKV files. << QLatin1String("com.apple.quicktime.creationdate"); // QT files. if (rmeta.contains(QLatin1String("creation_time"))) { - if (rmeta.contains(QLatin1String("com.apple.quicktime.creationdate"))) + if (rmeta.contains(QLatin1String("com.apple.quicktime.creationdate"))) { videoDateTimeOriginal.prepend(videoDateTimeOriginal.takeLast()); } else if (!rmeta.contains(QLatin1String("com.android.version"))) { if (rmeta[QLatin1String("creation_time")].endsWith(QLatin1Char('Z'))) { rmeta[QLatin1String("creation_time")].chop(1); } } } data = s_setXmpTagStringFromEntry(this, videoDateTimeOriginal, rmeta, QStringList() << QLatin1String("Xmp.video.DateTimeOriginal")); if (!data.isEmpty()) { // Backport date in Exif and Iptc. + QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); setImageDateTime(dt, true); } // -------------- s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("edit_date"), // Generic. rmeta, QStringList() << QLatin1String("Xmp.video.ModificationDate") << QLatin1String("Xmp.xmpDM.videoModDate")); // -------------- data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("date") // Generic. << QLatin1String("DATE_RELEASED"), // MKV files. rmeta); if (!data.isEmpty()) { QDateTime dt = QDateTime::fromString(data, Qt::ISODate).toLocalTime(); setXmpTagString("Xmp.video.MediaCreateDate", QString::number(s_secondsSinceJanuary1904(dt))); } // -------------- // GPS info as string. ex: "+44.8511-000.6229/" // Defined in ISO 6709:2008. // Notes: altitude can be passed as 3rd values. // each value is separated from others by '-' or '+'. // '/' is always the terminaison character. data = s_setXmpTagStringFromEntry(this, QStringList() << QLatin1String("location") // Generic. << QLatin1String("RECORDING_LOCATION") // MKV files. << QLatin1String("com.apple.quicktime.location.ISO6709"), // QT files. rmeta, QStringList() << QLatin1String("Xmp.video.GPSCoordinates") << QLatin1String("Xmp.xmpDM.shotLocation")); if (!data.isEmpty()) { // Backport location to Exif. QList digits; for (int i = 0 ; i < data.length() ; ++i) { QChar c = data[i]; - if (c == QLatin1Char('+') || c == QLatin1Char('-') || c == QLatin1Char('/')) + if ((c == QLatin1Char('+')) || (c == QLatin1Char('-')) || (c == QLatin1Char('/'))) { digits << i; } } QString coord; double lattitude = 0.0; double longitude = 0.0; double altitude = 0.0; bool b1 = false; bool b2 = false; bool b3 = false; if (digits.size() > 1) { coord = data.mid(digits[0], digits[1] - digits[0]); lattitude = coord.toDouble(&b1); } if (digits.size() > 2) { coord = data.mid(digits[1], digits[2] - digits[1]); longitude = coord.toDouble(&b2); } if (digits.size() > 3) { coord = data.mid(digits[2], digits[3] - digits[2]); altitude = coord.toDouble(&b3); } if (b1 && b2) { if (b3) { // All GPS values are available. + setGPSInfo(altitude, lattitude, longitude); setXmpTagString("Xmp.video.GPSAltitude", getXmpTagString("Xmp.exif.GPSAltitude")); setXmpTagString("Xmp.exif.GPSAltitude", getXmpTagString("Xmp.exif.GPSAltitude")); } else { // No altitude available. + double* alt = nullptr; setGPSInfo(alt, lattitude, longitude); } setXmpTagString("Xmp.video.GPSLatitude", getXmpTagString("Xmp.exif.GPSLatitude")); setXmpTagString("Xmp.video.GPSLongitude", getXmpTagString("Xmp.exif.GPSLongitude")); setXmpTagString("Xmp.video.GPSMapDatum", getXmpTagString("Xmp.exif.GPSMapDatum")); setXmpTagString("Xmp.video.GPSVersionID", getXmpTagString("Xmp.exif.GPSVersionID")); setXmpTagString("Xmp.exif.GPSLatitude", getXmpTagString("Xmp.exif.GPSLatitude")); setXmpTagString("Xmp.exif.GPSLongitude", getXmpTagString("Xmp.exif.GPSLongitude")); setXmpTagString("Xmp.exif.GPSMapDatum", getXmpTagString("Xmp.exif.GPSMapDatum")); setXmpTagString("Xmp.exif.GPSVersionID", getXmpTagString("Xmp.exif.GPSVersionID")); } } avformat_close_input(&fmt_ctx); QFileInfo fi(filePath); if (getXmpTagString("Xmp.video.FileName").isNull()) + { setXmpTagString("Xmp.video.FileName", fi.fileName()); + } if (getXmpTagString("Xmp.video.FileSize").isNull()) + { setXmpTagString("Xmp.video.FileSize", QString::number(fi.size() / (1024*1024))); + } if (getXmpTagString("Xmp.video.FileType").isNull()) + { setXmpTagString("Xmp.video.FileType", fi.suffix()); + } if (getXmpTagString("Xmp.video.MimeType").isNull()) + { setXmpTagString("Xmp.video.MimeType", QMimeDatabase().mimeTypeForFile(filePath).name()); + } return true; #else Q_UNUSED(filePath); + return false; #endif } QString DMetadata::videoColorModelToString(VIDEOCOLORMODEL videoColorModel) { QString cs; switch (videoColorModel) { case VIDEOCOLORMODEL_SRGB: cs = QLatin1String("sRGB"); break; + case VIDEOCOLORMODEL_BT601: cs = QLatin1String("CCIR-601"); break; + case VIDEOCOLORMODEL_BT709: cs = QLatin1String("CCIR-709"); break; + case VIDEOCOLORMODEL_OTHER: cs = QLatin1String("Other"); break; + default: // VIDEOCOLORMODEL_UNKNOWN break; } return cs; } VideoInfoContainer DMetadata::getVideoInformation() const { VideoInfoContainer videoInfo; if (hasXmp()) { if (videoInfo.aspectRatio.isEmpty()) { videoInfo.aspectRatio = getMetadataField(MetadataInfo::AspectRatio).toString(); } if (videoInfo.audioBitRate.isEmpty()) { videoInfo.audioBitRate = getXmpTagString("Xmp.audio.SampleRate"); } if (videoInfo.audioChannelType.isEmpty()) { videoInfo.audioChannelType = getXmpTagString("Xmp.audio.ChannelType"); } if (videoInfo.audioCodec.isEmpty()) { videoInfo.audioCodec = getXmpTagString("Xmp.audio.Codec"); } if (videoInfo.duration.isEmpty()) { videoInfo.duration = getXmpTagString("Xmp.video.duration"); } if (videoInfo.frameRate.isEmpty()) { videoInfo.frameRate = getXmpTagString("Xmp.video.FrameRate"); } if (videoInfo.videoCodec.isEmpty()) { videoInfo.videoCodec = getXmpTagString("Xmp.video.Codec"); } } return videoInfo; } } // namespace Digikam diff --git a/core/libs/metadataengine/dmetadata/dmetadata_xmp.cpp b/core/libs/metadataengine/dmetadata/dmetadata_xmp.cpp index b42bb86c0c..c8b5eeb757 100644 --- a/core/libs/metadataengine/dmetadata/dmetadata_xmp.cpp +++ b/core/libs/metadataengine/dmetadata/dmetadata_xmp.cpp @@ -1,170 +1,174 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-02-23 * Description : item metadata interface - Xmp helpers. * * Copyright (C) 2006-2020 by Gilles Caulier * Copyright (C) 2006-2013 by Marcel Wiesweg * Copyright (C) 2011 by Leif Huhn * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dmetadata.h" // Qt includes #include // Local includes #include "metaenginesettings.h" #include "digikam_version.h" #include "digikam_globals.h" #include "digikam_debug.h" namespace Digikam { QVariant DMetadata::fromXmpList(const char* const xmpTagName) const { QVariant var = getXmpTagVariant(xmpTagName); if (var.isNull()) { return QVariant(QVariant::StringList); } return var; } QVariant DMetadata::fromXmpLangAlt(const char* const xmpTagName) const { QVariant var = getXmpTagVariant(xmpTagName); if (var.isNull()) { return QVariant(QVariant::Map); } return var; } bool DMetadata::addToXmpTagStringBag(const char* const xmpTagName, const QStringList& entriesToAdd) const { QStringList oldEntries = getXmpTagStringBag(xmpTagName, false); QStringList newEntries = entriesToAdd; // Create a list of keywords including old one which already exists. - for (QStringList::const_iterator it = oldEntries.constBegin(); it != oldEntries.constEnd(); ++it ) + + for (QStringList::const_iterator it = oldEntries.constBegin() ; it != oldEntries.constEnd() ; ++it ) { if (!newEntries.contains(*it)) { newEntries.append(*it); } } if (setXmpTagStringBag(xmpTagName, newEntries)) { return true; } return false; } bool DMetadata::removeFromXmpTagStringBag(const char* const xmpTagName, const QStringList& entriesToRemove) const { QStringList currentEntries = getXmpTagStringBag(xmpTagName, false); QStringList newEntries; // Create a list of current keywords except those that shall be removed - for (QStringList::const_iterator it = currentEntries.constBegin(); it != currentEntries.constEnd(); ++it ) + + for (QStringList::const_iterator it = currentEntries.constBegin() ; it != currentEntries.constEnd() ; ++it ) { if (!entriesToRemove.contains(*it)) { newEntries.append(*it); } } if (setXmpTagStringBag(xmpTagName, newEntries)) { return true; } return false; } QStringList DMetadata::getXmpKeywords() const { return (getXmpTagStringBag("Xmp.dc.subject", false)); } bool DMetadata::setXmpKeywords(const QStringList& newKeywords) const { return setXmpTagStringBag("Xmp.dc.subject", newKeywords); } bool DMetadata::removeXmpKeywords(const QStringList& keywordsToRemove) { return removeFromXmpTagStringBag("Xmp.dc.subject", keywordsToRemove); } QStringList DMetadata::getXmpSubCategories() const { return (getXmpTagStringBag("Xmp.photoshop.SupplementalCategories", false)); } bool DMetadata::setXmpSubCategories(const QStringList& newSubCategories) const { return addToXmpTagStringBag("Xmp.photoshop.SupplementalCategories", newSubCategories); } bool DMetadata::removeXmpSubCategories(const QStringList& subCategoriesToRemove) { return removeFromXmpTagStringBag("Xmp.photoshop.SupplementalCategories", subCategoriesToRemove); } QStringList DMetadata::getXmpSubjects() const { return (getXmpTagStringBag("Xmp.iptc.SubjectCode", false)); } bool DMetadata::setXmpSubjects(const QStringList& newSubjects) const { return addToXmpTagStringBag("Xmp.iptc.SubjectCode", newSubjects); } bool DMetadata::removeXmpSubjects(const QStringList& subjectsToRemove) { return removeFromXmpTagStringBag("Xmp.iptc.SubjectCode", subjectsToRemove); } bool DMetadata::removeXmpTags(const QStringList& tagFilters) { MetaDataMap m = getXmpTagsDataList(tagFilters); if (m.isEmpty()) + { return false; + } for (MetaDataMap::iterator it = m.begin() ; it != m.end() ; ++it) { removeXmpTag(it.key().toLatin1().constData()); } return true; } } // namespace Digikam diff --git a/core/libs/metadataengine/dmetadata/dmetadatasettingscontainer.cpp b/core/libs/metadataengine/dmetadata/dmetadatasettingscontainer.cpp index bf8df95e10..7501dfca64 100644 --- a/core/libs/metadataengine/dmetadata/dmetadatasettingscontainer.cpp +++ b/core/libs/metadataengine/dmetadata/dmetadatasettingscontainer.cpp @@ -1,539 +1,540 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2015-08-12 * Description : DMetadata Settings Container. * * Copyright (C) 2015 by Veaceslav Munteanu * Copyright (C) 2015-2020 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "dmetadatasettingscontainer.h" // KDE includes #include #include // Local includes #include "dmetadatasettings.h" #include "digikam_debug.h" namespace Digikam { QString NamespaceEntry::DM_TAG_CONTAINER() { return QString::fromUtf8(I18N_NOOP("Tags")); } QString NamespaceEntry::DM_RATING_CONTAINER() { return QString::fromUtf8(I18N_NOOP("Rating")); } QString NamespaceEntry::DM_COMMENT_CONTAINER() { return QString::fromUtf8(I18N_NOOP("Comment")); } // ------------------------------------------------------------ bool dmcompare(NamespaceEntry& e1, NamespaceEntry e2) { return (e1.index < e2.index); } QDebug operator<<(QDebug dbg, const NamespaceEntry& inf) { dbg.nospace() << "[NamespaceEntry] nsType(" << inf.nsType << "), "; dbg.nospace() << "subspace(" << inf.subspace << "), "; dbg.nospace() << "isDefault(" << inf.isDefault << "), "; dbg.nospace() << "isDisabled(" << inf.isDisabled << "), "; dbg.nospace() << "index(" << inf.index << "), "; dbg.nospace() << "namespaceName(" << inf.namespaceName << "), "; dbg.nospace() << "alternativeName(" << inf.alternativeName << "), "; dbg.nospace() << "tagPaths(" << inf.tagPaths << "), "; dbg.nospace() << "separator(" << inf.separator << "), "; dbg.nospace() << "convertRatio(" << inf.convertRatio << "), "; dbg.nospace() << "specialOpts(" << inf.specialOpts << "), "; dbg.nospace() << "secondNameOpts(" << inf.secondNameOpts << ")"; return dbg.space(); } // ------------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN DMetadataSettingsContainer::Private { public: explicit Private() : unifyReadWrite(false) { } public: QMap > readMappings; QMap > writeMappings; bool unifyReadWrite; }; DMetadataSettingsContainer::DMetadataSettingsContainer() : d(new Private) { addMapping(NamespaceEntry::DM_TAG_CONTAINER()); addMapping(NamespaceEntry::DM_RATING_CONTAINER()); addMapping(NamespaceEntry::DM_COMMENT_CONTAINER()); } DMetadataSettingsContainer::DMetadataSettingsContainer(const DMetadataSettingsContainer& other) : d(new Private) { *d = *other.d; } DMetadataSettingsContainer& DMetadataSettingsContainer::operator=(const DMetadataSettingsContainer& other) { *d = *other.d; return *this; } DMetadataSettingsContainer::~DMetadataSettingsContainer() { delete d; } bool DMetadataSettingsContainer::unifyReadWrite() const { return d->unifyReadWrite; } void DMetadataSettingsContainer::setUnifyReadWrite(bool b) { d->unifyReadWrite = b; } void DMetadataSettingsContainer::readFromConfig(KConfigGroup& group) { bool valid = true; const QString readNameSpace = QLatin1String("read%1Namespaces"); const QString writeNameSpace = QLatin1String("write%1Namespaces"); foreach (const QString& str, mappingKeys()) { if (!group.hasGroup(readNameSpace.arg(str))) { valid = false; qCDebug(DIGIKAM_GENERAL_LOG) << "Does not contain " << str << " Namespace"; break; } if (!group.hasGroup(writeNameSpace.arg(str))) { valid = false; qCDebug(DIGIKAM_GENERAL_LOG) << "Does not contain " << str << " Namespace"; break; } } if (valid) { foreach (const QString& str, mappingKeys()) { readOneGroup(group, readNameSpace.arg(str), getReadMapping(str)); readOneGroup(group, writeNameSpace.arg(str), getWriteMapping(str)); } } else { defaultValues(); } } void DMetadataSettingsContainer::writeToConfig(KConfigGroup& group) const { const QString readNameSpace = QLatin1String("read%1Namespaces"); const QString writeNameSpace = QLatin1String("write%1Namespaces"); foreach (const QString& str, mappingKeys()) { // Remove all old group elements. group.group(readNameSpace.arg(str)).deleteGroup(); group.group(writeNameSpace.arg(str)).deleteGroup(); writeOneGroup(group, readNameSpace.arg(str), getReadMapping(str)); writeOneGroup(group, writeNameSpace.arg(str), getWriteMapping(str)); } group.sync(); } void DMetadataSettingsContainer::defaultValues() { d->unifyReadWrite = true; d->writeMappings.clear(); d->readMappings.clear(); defaultTagValues(); defaultRatingValues(); defaultCommentValues(); } void DMetadataSettingsContainer::addMapping(const QString& key) { d->readMappings[key] = QList(); d->writeMappings[key] = QList(); } QList &DMetadataSettingsContainer::getReadMapping(const QString& key) const { return d->readMappings[key]; } QList &DMetadataSettingsContainer::getWriteMapping(const QString& key) const { return d->writeMappings[key]; } QList DMetadataSettingsContainer::mappingKeys() const { return d->readMappings.keys(); } void DMetadataSettingsContainer::defaultTagValues() { // Default tag namespaces + NamespaceEntry tagNs1; tagNs1.namespaceName = QLatin1String("Xmp.digiKam.TagsList"); tagNs1.tagPaths = NamespaceEntry::TAGPATH; tagNs1.separator = QLatin1Char('/'); tagNs1.nsType = NamespaceEntry::TAGS; tagNs1.index = 0; tagNs1.specialOpts = NamespaceEntry::TAG_XMPSEQ; tagNs1.subspace = NamespaceEntry::XMP; NamespaceEntry tagNs2; tagNs2.namespaceName = QLatin1String("Xmp.MicrosoftPhoto.LastKeywordXMP"); tagNs2.tagPaths = NamespaceEntry::TAGPATH; tagNs2.separator = QLatin1Char('/'); tagNs2.nsType = NamespaceEntry::TAGS; tagNs2.index = 1; tagNs2.specialOpts = NamespaceEntry::TAG_XMPBAG; tagNs2.subspace = NamespaceEntry::XMP; NamespaceEntry tagNs3; tagNs3.namespaceName = QLatin1String("Xmp.lr.hierarchicalSubject"); tagNs3.tagPaths = NamespaceEntry::TAGPATH; tagNs3.separator = QLatin1Char('|'); tagNs3.nsType = NamespaceEntry::TAGS; tagNs3.index = 2; tagNs3.specialOpts = NamespaceEntry::TAG_XMPBAG; tagNs3.subspace = NamespaceEntry::XMP; tagNs3.alternativeName = QLatin1String("Xmp.lr.HierarchicalSubject"); tagNs3.secondNameOpts = NamespaceEntry::TAG_XMPSEQ; NamespaceEntry tagNs4; tagNs4.namespaceName = QLatin1String("Xmp.mediapro.CatalogSets"); tagNs4.tagPaths = NamespaceEntry::TAGPATH; tagNs4.separator = QLatin1Char('|'); tagNs4.nsType = NamespaceEntry::TAGS; tagNs4.index = 3; tagNs4.specialOpts = NamespaceEntry::TAG_XMPBAG; tagNs4.subspace = NamespaceEntry::XMP; NamespaceEntry tagNs5; tagNs5.namespaceName = QLatin1String("Xmp.acdsee.categories"); tagNs5.tagPaths = NamespaceEntry::TAGPATH; tagNs5.separator = QLatin1Char('/'); tagNs5.nsType = NamespaceEntry::TAGS; tagNs5.index = 4; tagNs5.specialOpts = NamespaceEntry::TAG_ACDSEE; tagNs5.subspace = NamespaceEntry::XMP; NamespaceEntry tagNs6; tagNs6.namespaceName = QLatin1String("Xmp.dc.subject"); tagNs6.tagPaths = NamespaceEntry::TAG; tagNs6.separator = QLatin1Char('/'); tagNs6.nsType = NamespaceEntry::TAGS; tagNs6.index = 5; tagNs6.specialOpts = NamespaceEntry::TAG_XMPBAG; tagNs6.subspace = NamespaceEntry::XMP; NamespaceEntry tagNs7; tagNs7.namespaceName = QLatin1String("Iptc.Application2.Keywords"); tagNs7.tagPaths = NamespaceEntry::TAGPATH; tagNs7.separator = QLatin1Char('.'); tagNs7.nsType = NamespaceEntry::TAGS; tagNs7.index = 6; tagNs7.subspace = NamespaceEntry::IPTC; NamespaceEntry tagNs8; tagNs8.namespaceName = QLatin1String("Exif.Image.XPKeywords"); tagNs8.tagPaths = NamespaceEntry::TAGPATH; tagNs8.separator = QLatin1Char(';'); tagNs8.nsType = NamespaceEntry::TAGS; tagNs8.index = 7; tagNs8.subspace = NamespaceEntry::EXIF; getReadMapping(NamespaceEntry::DM_TAG_CONTAINER()) << tagNs1 << tagNs2 << tagNs3 << tagNs4 << tagNs5 << tagNs6 << tagNs7 << tagNs8; d->writeMappings[NamespaceEntry::DM_TAG_CONTAINER()] = QList(getReadMapping(NamespaceEntry::DM_TAG_CONTAINER())); } void DMetadataSettingsContainer::defaultRatingValues() { QList defaultVal; QList microsoftMappings; QList iptcMappings; defaultVal << 0 << 1 << 2 << 3 << 4 << 5; microsoftMappings << 0 << 1 << 25 << 50 << 75 << 99; iptcMappings << 8 << 6 << 5 << 4 << 2 << 1; NamespaceEntry ratingNs1; ratingNs1.namespaceName = QLatin1String("Xmp.xmp.Rating"); ratingNs1.convertRatio = defaultVal; ratingNs1.nsType = NamespaceEntry::RATING; ratingNs1.index = 0; ratingNs1.subspace = NamespaceEntry::XMP; NamespaceEntry ratingNs2; ratingNs2.namespaceName = QLatin1String("Xmp.acdsee.rating"); ratingNs2.convertRatio = defaultVal; ratingNs2.nsType = NamespaceEntry::RATING; ratingNs2.index = 1; ratingNs2.subspace = NamespaceEntry::XMP; NamespaceEntry ratingNs3; ratingNs3.namespaceName = QLatin1String("Xmp.MicrosoftPhoto.Rating"); ratingNs3.convertRatio = microsoftMappings; ratingNs3.nsType = NamespaceEntry::RATING; ratingNs3.index = 2; ratingNs3.subspace = NamespaceEntry::XMP; NamespaceEntry ratingNs4; ratingNs4.namespaceName = QLatin1String("Exif.Image.0x4746"); ratingNs4.convertRatio = defaultVal; ratingNs4.nsType = NamespaceEntry::RATING; ratingNs4.index = 3; ratingNs4.subspace = NamespaceEntry::EXIF; NamespaceEntry ratingNs5; ratingNs5.namespaceName = QLatin1String("Exif.Image.0x4749"); ratingNs5.convertRatio = microsoftMappings; ratingNs5.nsType = NamespaceEntry::RATING; ratingNs5.index = 4; ratingNs5.subspace = NamespaceEntry::EXIF; NamespaceEntry ratingNs6; ratingNs6.namespaceName = QLatin1String("Iptc.Application2.Urgency"); ratingNs6.convertRatio = iptcMappings; ratingNs6.nsType = NamespaceEntry::RATING; ratingNs6.index = 5; ratingNs6.subspace = NamespaceEntry::IPTC; getReadMapping(NamespaceEntry::DM_RATING_CONTAINER()) << ratingNs1 << ratingNs2 << ratingNs3 << ratingNs4 << ratingNs5 << ratingNs6; d->writeMappings[NamespaceEntry::DM_RATING_CONTAINER()] = QList(getReadMapping(NamespaceEntry::DM_RATING_CONTAINER())); } void DMetadataSettingsContainer::defaultCommentValues() { NamespaceEntry commNs1; commNs1.namespaceName = QLatin1String("Xmp.dc.description"); commNs1.nsType = NamespaceEntry::COMMENT; commNs1.specialOpts = NamespaceEntry::COMMENT_ATLLANGLIST; commNs1.index = 0; commNs1.subspace = NamespaceEntry::XMP; NamespaceEntry commNs2; commNs2.namespaceName = QLatin1String("Xmp.exif.UserComment"); commNs2.nsType = NamespaceEntry::COMMENT; commNs2.specialOpts = NamespaceEntry::COMMENT_ALTLANG; commNs2.index = 1; commNs2.subspace = NamespaceEntry::XMP; NamespaceEntry commNs3; commNs3.namespaceName = QLatin1String("Xmp.tiff.ImageDescription"); commNs3.nsType = NamespaceEntry::COMMENT; commNs3.specialOpts = NamespaceEntry::COMMENT_ALTLANG; commNs3.index = 2; commNs3.subspace = NamespaceEntry::XMP; NamespaceEntry commNs4; commNs4.namespaceName = QLatin1String("Xmp.acdsee.notes"); commNs4.nsType = NamespaceEntry::COMMENT; commNs4.specialOpts = NamespaceEntry::COMMENT_XMP; commNs4.index = 3; commNs4.subspace = NamespaceEntry::XMP; NamespaceEntry commNs5; commNs5.namespaceName = QLatin1String("JPEG/TIFF Comments"); commNs5.nsType = NamespaceEntry::COMMENT; commNs5.specialOpts = NamespaceEntry::COMMENT_JPEG; commNs5.index = 4; commNs5.subspace = NamespaceEntry::XMP; NamespaceEntry commNs6; commNs6.namespaceName = QLatin1String("Exif.Image.ImageDescription"); commNs6.nsType = NamespaceEntry::COMMENT; commNs6.specialOpts = NamespaceEntry::NO_OPTS; commNs6.index = 5; commNs6.alternativeName = QLatin1String("Exif.Photo.UserComment"); commNs6.subspace = NamespaceEntry::EXIF; NamespaceEntry commNs7; commNs7.namespaceName = QLatin1String("Iptc.Application2.Caption"); commNs7.nsType = NamespaceEntry::COMMENT; commNs7.specialOpts = NamespaceEntry::NO_OPTS; commNs7.index = 6; commNs7.subspace = NamespaceEntry::IPTC; getReadMapping(NamespaceEntry::DM_COMMENT_CONTAINER()) << commNs1 << commNs2 << commNs3 << commNs4 << commNs5 << commNs6 << commNs7; d->writeMappings[NamespaceEntry::DM_COMMENT_CONTAINER()] = QList(getReadMapping(NamespaceEntry::DM_COMMENT_CONTAINER())); } void DMetadataSettingsContainer::readOneGroup(KConfigGroup& group, const QString& name, QList& container) { KConfigGroup myItems = group.group(name); for (QString element : myItems.groupList()) { KConfigGroup gr = myItems.group(element); NamespaceEntry ns; if (element.startsWith(QLatin1Char('#'))) { ns.namespaceName = gr.readEntry("namespaceName"); } else { ns.namespaceName = element; } ns.tagPaths = (NamespaceEntry::TagType)gr.readEntry("tagPaths").toInt(); ns.separator = gr.readEntry("separator"); ns.nsType = (NamespaceEntry::NamespaceType)gr.readEntry("nsType").toInt(); ns.index = gr.readEntry("index").toInt(); ns.subspace = (NamespaceEntry::NsSubspace)gr.readEntry("subspace").toInt(); ns.alternativeName = gr.readEntry("alternativeName"); ns.specialOpts = (NamespaceEntry::SpecialOptions)gr.readEntry("specialOpts").toInt(); ns.secondNameOpts = (NamespaceEntry::SpecialOptions)gr.readEntry("secondNameOpts").toInt(); ns.isDefault = gr.readEntry(QLatin1String("isDefault"), QVariant(true)).toBool(); ns.isDisabled = gr.readEntry(QLatin1String("isDisabled"), QVariant(false)).toBool(); QString conversion = gr.readEntry("convertRatio"); for (QString str : conversion.split(QLatin1String(","))) { ns.convertRatio.append(str.toInt()); } container.append(ns); } std::sort(container.begin(), container.end(), Digikam::dmcompare); } void DMetadataSettingsContainer::writeOneGroup(KConfigGroup& group, const QString& name, QList& container) const { KConfigGroup namespacesGroup = group.group(name); int index = 0; for (NamespaceEntry e : container) { QString groupNumber = QString::fromLatin1("#%1") .arg(index++, 4, 10, QLatin1Char('0')); KConfigGroup tmp = namespacesGroup.group(groupNumber); tmp.writeEntry("namespaceName", e.namespaceName); tmp.writeEntry("alternativeName", e.alternativeName); tmp.writeEntry("subspace", (int)e.subspace); tmp.writeEntry("tagPaths", (int)e.tagPaths); tmp.writeEntry("separator", e.separator); tmp.writeEntry("nsType", (int)e.nsType); tmp.writeEntry("convertRatio", e.convertRatio); tmp.writeEntry("specialOpts", (int)e.specialOpts); tmp.writeEntry("secondNameOpts", (int)e.secondNameOpts); tmp.writeEntry("index", e.index); tmp.writeEntry("isDisabled", e.isDisabled); tmp.writeEntry("isDefault", e.isDefault); } } QDebug operator<<(QDebug dbg, const DMetadataSettingsContainer& inf) { dbg.nospace() << "[DMetadataSettingsContainer] readMappings("; foreach (const QString& str, inf.mappingKeys()) { dbg.nospace() << inf.getReadMapping(str) << "), "; } dbg.nospace() << "writeMappings("; foreach (const QString& str, inf.mappingKeys()) { dbg.nospace() << inf.getWriteMapping(str) << "), "; } dbg.nospace() << "unifyReadWrite(" << inf.unifyReadWrite() << ")"; return dbg.space(); } } // namespace Digikam diff --git a/core/libs/metadataengine/dmetadata/geodetictools.cpp b/core/libs/metadataengine/dmetadata/geodetictools.cpp index bc5da138bf..d2da3a5c31 100644 --- a/core/libs/metadataengine/dmetadata/geodetictools.cpp +++ b/core/libs/metadataengine/dmetadata/geodetictools.cpp @@ -1,892 +1,892 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2008-05-05 * Description : Geodetic tools based from an implementation written by * Daniele Franzoni and Martin Desruisseaux from * GeoTools Project Managment Committee (PMC), http://geotools.org * * Copyright (C) 2008-2011 by Marcel Wiesweg * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #include "geodetictools.h" // C++ includes #include #include namespace Digikam { using namespace Coordinates; GeodeticCalculator::GeodeticCalculator(const Ellipsoid& e) : m_ellipsoid(e), m_lat1(0), m_long1(0), m_lat2(0), m_long2(0), m_distance(0), m_azimuth(0), m_destinationValid(false), m_directionValid(false) { m_semiMajorAxis = m_ellipsoid.semiMajorAxis(); m_semiMinorAxis = m_ellipsoid.semiMinorAxis(); // constants TOLERANCE_0 = 5.0e-15, TOLERANCE_1 = 5.0e-14, TOLERANCE_2 = 5.0e-13, TOLERANCE_3 = 7.0e-3; TOLERANCE_CHECK = 1E-8; /* calculation of GPNHRI parameters */ f = (m_semiMajorAxis-m_semiMinorAxis) / m_semiMajorAxis; fo = 1.0 - f; f2 = f*f; f3 = f*f2; f4 = f*f3; m_eccentricitySquared = f * (2.0-f); /* Calculation of GNPARC parameters */ const double E2 = m_eccentricitySquared; const double E4 = E2*E2; const double E6 = E4*E2; const double E8 = E6*E2; const double EX = E8*E2; A = 1.0+0.75*E2+0.703125*E4+0.68359375 *E6+0.67291259765625*E8+0.6661834716796875 *EX; B = 0.75*E2+0.9375 *E4+1.025390625*E6+1.07666015625 *E8+1.1103057861328125 *EX; C = 0.234375*E4+0.41015625 *E6+0.538330078125 *E8+0.63446044921875 *EX; D = 0.068359375*E6+0.15380859375 *E8+0.23792266845703125*EX; E = 0.01922607421875*E8+0.0528717041015625 *EX; F = 0.00528717041015625*EX; m_maxOrthodromicDistance = m_semiMajorAxis * (1.0-E2) * M_PI * A - 1.0; T1 = 1.0; T2 = -0.25*f*(1.0 + f + f2); T4 = 0.1875 * f2 * (1.0+2.25*f); T6 = 0.1953125 * f3; const double a = f3*(1.0+2.25*f); a01 = -f2*(1.0+f+f2)/4.0; a02 = 0.1875*a; a03 = -0.1953125*f4; a21 = -a01; a22 = -0.25*a; a23 = 0.29296875*f4; a42 = 0.03125*a; a43 = 0.05859375*f4; a63 = 5.0*f4/768.0; } double GeodeticCalculator::castToAngleRange(const double alpha) { - return alpha - (2*M_PI) * floor(alpha/(2*M_PI) + 0.5); + return (alpha - (2*M_PI) * floor(alpha/(2*M_PI) + 0.5)); } bool GeodeticCalculator::checkLatitude(double* latitude) { - if (*latitude >= -90.0 && *latitude <= 90.0) + if ((*latitude >= -90.0) && (*latitude <= 90.0)) { *latitude = toRadians(*latitude); return true; } return false; } bool GeodeticCalculator::checkLongitude(double* longitude) { - if (*longitude >= -180.0 && *longitude <= 180.0) + if ((*longitude >= -180.0) && (*longitude <= 180.0)) { *longitude = toRadians(*longitude); return true; } return false; } bool GeodeticCalculator::checkAzimuth(double* azimuth) { - if (*azimuth >= -180.0 && *azimuth <= 180.0) + if ((*azimuth >= -180.0) && (*azimuth <= 180.0)) { *azimuth = toRadians(*azimuth); return true; } return false; } bool GeodeticCalculator::checkOrthodromicDistance(const double distance) { - return distance >= 0.0 && distance <= m_maxOrthodromicDistance; + return (distance >= 0.0) && (distance <= m_maxOrthodromicDistance); } Ellipsoid GeodeticCalculator::ellipsoid() const { return m_ellipsoid; } void GeodeticCalculator::setStartingGeographicPoint(double longitude, double latitude) { if (!checkLongitude(&longitude) || !checkLatitude(&latitude)) { return; } // Check passed. Now performs the changes in this object. m_long1 = longitude; m_lat1 = latitude; m_destinationValid = false; m_directionValid = false; } void GeodeticCalculator::setDestinationGeographicPoint(double longitude, double latitude) { if (!checkLongitude(&longitude) || !checkLatitude(&latitude)) { return; } // Check passed. Now performs the changes in this object. m_long2 = longitude; m_lat2 = latitude; m_destinationValid = true; m_directionValid = false; } bool GeodeticCalculator::destinationGeographicPoint(double* longitude, double* latitude) { if (!m_destinationValid) { if (!computeDestinationPoint()) { return false; } } *longitude = toDegrees(m_long2); *latitude = toDegrees(m_lat2); return true; } QPointF GeodeticCalculator::destinationGeographicPoint() { double x = 0.0; double y = 0.0; destinationGeographicPoint(&x, &y); QPointF point; point.setX(x); point.setY(y); return point; } void GeodeticCalculator::setDirection(double azimuth, double distance) { // Check first in case an exception is raised // (in other words, we change all or nothing). if (!checkAzimuth(&azimuth)) { return; } if (!checkOrthodromicDistance(distance)) { return; } // Check passed. Now performs the changes in this object. m_azimuth = azimuth; m_distance = distance; m_destinationValid = false; m_directionValid = true; } double GeodeticCalculator::azimuth() { if (!m_directionValid) { computeDirection(); } return toDegrees(m_azimuth); } double GeodeticCalculator::orthodromicDistance() { if (!m_directionValid) { computeDirection(); checkOrthodromicDistance(); } return m_distance; } bool GeodeticCalculator::checkOrthodromicDistance() { double check = m_ellipsoid.orthodromicDistance(toDegrees(m_long1), toDegrees(m_lat1), toDegrees(m_long2), toDegrees(m_lat2)); check = fabs(m_distance - check); return (check <= (m_distance+1) * TOLERANCE_CHECK); } bool GeodeticCalculator::computeDestinationPoint() { if (!m_directionValid) { return false; } // Protect internal variables from changes const double lat1 = m_lat1; const double long1 = m_long1; const double azimuth = m_azimuth; const double distance = m_distance; /* * Solution of the geodetic direct problem after T.Vincenty. * Modified Rainsford's method with Helmert's elliptical terms. * Effective in any azimuth and at any distance short of antipodal. * * Latitudes and longitudes in radians positive North and East. * Forward azimuths at both points returned in radians from North. * * Programmed for CDC-6600 by LCDR L.Pfeifer NGS ROCKVILLE MD 18FEB75 * Modified for IBM SYSTEM 360 by John G.Gergen NGS ROCKVILLE MD 7507 * Ported from Fortran to Java by Daniele Franzoni. * * Source: ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/forward.for * subroutine DIRECT1 */ double TU = fo*sin(lat1) / cos(lat1); double SF = sin(azimuth); double CF = cos(azimuth); double BAZ = (CF!=0) ? atan2(TU,CF)*2.0 : 0; double CU = 1/sqrt(TU*TU + 1.0); double SU = TU*CU; double SA = CU*SF; double C2A = 1.0 - SA*SA; double X = sqrt((1.0/fo/fo-1)*C2A+1.0) + 1.0; X = (X-2.0)/X; double C = 1.0-X; C = (X*X/4.0+1.0)/C; double D = (0.375*X*X-1.0)*X; TU = distance / fo / m_semiMajorAxis / C; double Y = TU; double SY, CY, CZ, E; do { SY = sin(Y); CY = cos(Y); CZ = cos(BAZ+Y); E = CZ*CZ*2.0-1.0; C = Y; X = E*CY; Y = E+E-1.0; Y = (((SY*SY*4.0-3.0)*Y*CZ*D/6.0+X)*D/4.0-CZ)*SY*D+TU; } while (fabs(Y-C) > TOLERANCE_1); BAZ = CU*CY*CF - SU*SY; C = fo*sqrt(SA*SA+BAZ*BAZ); D = SU*CY + CU*SY*CF; m_lat2 = atan2(D,C); C = CU*CY-SU*SY*CF; X = atan2(SY*SF,C); C = ((-3.0*C2A+4.0)*f+4.0)*C2A*f/16.0; D = ((E*CY*C+CZ)*SY*C+Y)*SA; m_long2 = long1+X - (1.0-C)*D*f; m_long2 = castToAngleRange(m_long2); m_destinationValid = true; return true; } double GeodeticCalculator::meridianArcLength(double latitude1, double latitude2) { if (!checkLatitude(&latitude1) || !checkLatitude(&latitude2)) { return 0.0; } return meridianArcLengthRadians(latitude1, latitude2); } double GeodeticCalculator::meridianArcLengthRadians(double P1, double P2) { /* * Latitudes P1 and P2 in radians positive North and East. * Forward azimuths at both points returned in radians from North. * * Source: ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/inverse.for * subroutine GPNARC * version 200005.26 * written by Robert (Sid) Safford * * Ported from Fortran to Java by Daniele Franzoni. */ double S1 = fabs(P1); double S2 = fabs(P2); double DA = (P2-P1); // Check for a 90 degree lookup if ((S1 > TOLERANCE_0) || (S2 <= (M_PI/2-TOLERANCE_0)) || (S2 >= (M_PI/2+TOLERANCE_0))) { const double DB = sin(P2* 2.0) - sin(P1* 2.0); const double DC = sin(P2* 4.0) - sin(P1* 4.0); const double DD = sin(P2* 6.0) - sin(P1* 6.0); const double DE = sin(P2* 8.0) - sin(P1* 8.0); const double DF = sin(P2*10.0) - sin(P1*10.0); // Compute the S2 part of the series expansion S2 = -DB*B/2.0 + DC*C/4.0 - DD*D/6.0 + DE*E/8.0 - DF*F/10.0; } // Compute the S1 part of the series expansion - S1 = DA*A; + S1 = DA*A; // Compute the arc length return fabs(m_semiMajorAxis * (1.0-m_eccentricitySquared) * (S1+S2)); } /** * Computes the azimuth and orthodromic distance from the * startingGeographicPoint() and the * destinationGeographicPoint(). */ bool GeodeticCalculator::computeDirection() { if (!m_destinationValid) { return false; } // Protect internal variables from change. const double long1 = m_long1; const double lat1 = m_lat1; const double long2 = m_long2; const double lat2 = m_lat2; /* * Solution of the geodetic inverse problem after T.Vincenty. * Modified Rainsford's method with Helmert's elliptical terms. * Effective in any azimuth and at any distance short of antipodal. * * Latitudes and longitudes in radians positive North and East. * Forward azimuths at both points returned in radians from North. * * Programmed for CDC-6600 by LCDR L.Pfeifer NGS ROCKVILLE MD 18FEB75 * Modified for IBM SYSTEM 360 by John G.Gergen NGS ROCKVILLE MD 7507 * Ported from Fortran to Java by Daniele Franzoni. * * Source: ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/inverse.for * subroutine GPNHRI * version 200208.09 * written by robert (sid) safford */ const double dlon = castToAngleRange(long2-long1); const double ss = fabs(dlon); if (ss < TOLERANCE_1) { m_distance = meridianArcLengthRadians(lat1, lat2); m_azimuth = (lat2>lat1) ? 0.0 : M_PI; m_directionValid = true; return true; } /* * Computes the limit in longitude (alimit), it is equal * to twice the distance from the equator to the pole, * as measured along the equator */ // tests for antinodal difference const double ESQP = m_eccentricitySquared / (1.0-m_eccentricitySquared); const double alimit = M_PI*fo; if ((ss >= alimit) && (lat1 < TOLERANCE_3) && (lat1 > -TOLERANCE_3) && (lat2 < TOLERANCE_3) && (lat2 > -TOLERANCE_3)) { // Computes an approximate AZ const double CONS = (M_PI-ss)/(M_PI*f); double AZ = asin(CONS); int iter = 0; double AZ_TEMP, S, AO; do { if (++iter > 8) { //ERROR return false; } S = cos(AZ); const double C2 = S*S; // Compute new AO AO = T1 + T2*C2 + T4*C2*C2 + T6*C2*C2*C2; const double _CS_ = CONS/AO; S = asin(_CS_); AZ_TEMP = AZ; AZ = S; } while (fabs(S-AZ_TEMP) >= TOLERANCE_2); const double AZ1 = (dlon < 0.0) ? 2.0*M_PI - S : S; m_azimuth = castToAngleRange(AZ1); //const double AZ2 = 2.0*M_PI - AZ1; S = cos(AZ1); // Equatorial - geodesic(S-s) SMS - const double U2 = ESQP*S*S; - const double U4 = U2*U2; - const double U6 = U4*U2; - const double U8 = U6*U2; - const double BO = 1.0 + - 0.25 *U2 + - 0.046875 *U4 + - 0.01953125 *U6 + - -0.01068115234375*U8; + const double U2 = ESQP*S*S; + const double U4 = U2*U2; + const double U6 = U4*U2; + const double U8 = U6*U2; + const double BO = 1.0 + + 0.25 *U2 + + 0.046875 *U4 + + 0.01953125 *U6 + + -0.01068115234375*U8; S = sin(AZ1); const double SMS = m_semiMajorAxis*M_PI*(1.0 - f*fabs(S)*AO - BO*fo); m_distance = m_semiMajorAxis*ss - SMS; m_directionValid = true; return true; } // the reduced latitudes const double u1 = atan(fo*sin(lat1)/cos(lat1)); const double u2 = atan(fo*sin(lat2)/cos(lat2)); const double su1 = sin(u1); const double cu1 = cos(u1); const double su2 = sin(u2); const double cu2 = cos(u2); double xy, w, q2, q4, q6, r2, r3, sig, ssig, slon, clon, sinalf, ab=dlon; int kcount = 0; do { if (++kcount > 8) { //ERROR return false; } clon = cos(ab); slon = sin(ab); const double csig = su1*su2 + cu1*cu2*clon; ssig = sqrt(slon*cu2*slon*cu2 + (su2*cu1-su1*cu2*clon)*(su2*cu1-su1*cu2*clon)); sig = atan2(ssig, csig); sinalf = cu1*cu2*slon/ssig; w = (1.0 - sinalf*sinalf); const double t4 = w*w; const double t6 = w*t4; // the coefficients of type a const double ao = f+a01*w+a02*t4+a03*t6; const double a2 = a21*w+a22*t4+a23*t6; const double a4 = a42*t4+a43*t6; const double a6 = a63*t6; // the multiple angle functions double qo = 0.0; if (w > TOLERANCE_0) { qo = -2.0*su1*su2/w; } q2 = csig + qo; q4 = 2.0*q2*q2 - 1.0; q6 = q2*(4.0*q2*q2 - 3.0); r2 = 2.0*ssig*csig; r3 = ssig*(3.0 - 4.0*ssig*ssig); // the longitude difference const double s = sinalf*(ao*sig + a2*ssig*q2 + a4*r2*q4 + a6*r3*q6); double xz = dlon+s; xy = fabs(xz-ab); ab = dlon+s; } while (xy >= TOLERANCE_1); const double z = ESQP*w; const double bo = 1.0 + z*( 1.0/4.0 + z*(-3.0/ 64.0 + z*( 5.0/256.0 - z*(175.0/16384.0)))); const double b2 = z*(-1.0/4.0 + z*( 1.0/ 16.0 + z*(-15.0/512.0 + z*( 35.0/ 2048.0)))); const double b4 = z*z*(-1.0/ 128.0 + z*( 3.0/512.0 - z*( 35.0/ 8192.0))); const double b6 = z*z*z*(-1.0/1536.0 + z*( 5.0/ 6144.0)); // The distance in ellispoid axis units. m_distance = m_semiMinorAxis * (bo*sig + b2*ssig*q2 + b4*r2*q4 + b6*r3*q6); double az1 = (dlon < 0.0) ? M_PI*(3.0/2.0) : M_PI/2.0; // now compute the az1 & az2 for latitudes not on the equator - if ((fabs(su1)>=TOLERANCE_0) || (fabs(su2)>=TOLERANCE_0)) + if ((fabs(su1) >= TOLERANCE_0) || (fabs(su2) >= TOLERANCE_0)) { const double tana1 = slon*cu2 / (su2*cu1 - clon*su1*cu2); const double sina1 = sinalf/cu1; // azimuths from north,longitudes positive east az1 = atan2(sina1, sina1/tana1); } m_azimuth = castToAngleRange(az1); m_directionValid = true; return true; } /* / ** * Calculates the geodetic curve between two points in the referenced ellipsoid. * A curve in the ellipsoid is a path which points contain the longitude and latitude * of the points in the geodetic curve. The geodetic curve is computed from the * {@linkplain #getStartingGeographicPoint starting point} to the * {@linkplain #getDestinationGeographicPoint destination point}. * * @param numberOfPoints The number of vertex in the geodetic curve. * NOTE: This argument is only a hint and may be ignored * in future version (if we compute a real curve rather than a list of line * segments). * @return The path that represents the geodetic curve from the * {@linkplain #getStartingGeographicPoint starting point} to the * {@linkplain #getDestinationGeographicPoint destination point}. * * @todo We should check for cases where the path cross the 90N, 90S, 90E or 90W boundaries. * / public Shape getGeodeticCurve(const int numberOfPoints) { if (numberOfPoints < 0) return Shape; if (!directionValid) { computeDirection(); } if (!destinationValid) { computeDestinationPoint(); } const double long2 = this->long2; const double lat2 = this->lat2; const double distance = this->distance; const double deltaDistance = distance / (numberOfPoints+1); final GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD, numberOfPoints+1); path.moveTo((float)toDegrees(long1), (float)toDegrees(lat1)); for (int i=1; idistance = i*deltaDistance; computeDestinationPoint(); path.lineTo((float)toDegrees(this->long2), (float)toDegrees(this->lat2)); } this->long2 = long2; this->lat2 = lat2; this->distance = distance; path.lineTo((float)toDegrees(long2), (float)toDegrees(lat2)); return path; } / ** * Calculates the geodetic curve between two points in the referenced ellipsoid. * A curve in the ellipsoid is a path which points contain the longitude and latitude * of the points in the geodetic curve. The geodetic curve is computed from the * {@linkplain #getStartingGeographicPoint starting point} to the * {@linkplain #getDestinationGeographicPoint destination point}. * * @return The path that represents the geodetic curve from the * {@linkplain #getStartingGeographicPoint starting point} to the * {@linkplain #getDestinationGeographicPoint destination point}. * / public Shape getGeodeticCurve() { return getGeodeticCurve(10); } */ // --------------------------------------------------------------------------------- Ellipsoid Ellipsoid::WGS84() { return createFlattenedSphere(QLatin1String("WGS84"), 6378137.0, 298.257223563); } Ellipsoid Ellipsoid::GRS80() { return createFlattenedSphere(QLatin1String("GRS80"), 6378137.0, 298.257222101); } Ellipsoid Ellipsoid::INTERNATIONAL_1924() { return createFlattenedSphere(QLatin1String("International 1924"), 6378388.0, 297.0); } Ellipsoid Ellipsoid::CLARKE_1866() { return createFlattenedSphere(QLatin1String("Clarke 1866"), 6378206.4, 294.9786982); } Ellipsoid Ellipsoid::SPHERE() { return createEllipsoid(QLatin1String("SPHERE"), 6371000, 6371000); } Ellipsoid::Ellipsoid(const QString& name, double semiMajorAxis, double semiMinorAxis, double inverseFlattening, bool ivfDefinitive) : name(name), m_semiMajorAxis(semiMajorAxis), m_semiMinorAxis(semiMinorAxis), m_inverseFlattening(inverseFlattening), m_ivfDefinitive(ivfDefinitive), m_isSphere(false) { } Ellipsoid::Ellipsoid(const QString& name, double radius, bool ivfDefinitive) : name(name), m_semiMajorAxis(radius), m_semiMinorAxis(radius), m_inverseFlattening(DBL_MAX), m_ivfDefinitive(ivfDefinitive), m_isSphere(true) { } Ellipsoid Ellipsoid::createEllipsoid(const QString& name, double m_semiMajorAxis, double m_semiMinorAxis) { if (m_semiMajorAxis == m_semiMinorAxis) { return Ellipsoid(name, m_semiMajorAxis, false); } else { return Ellipsoid(name, m_semiMajorAxis, m_semiMinorAxis, m_semiMajorAxis/(m_semiMajorAxis-m_semiMinorAxis), false); } } Ellipsoid Ellipsoid::createFlattenedSphere(const QString& name, double m_semiMajorAxis, double m_inverseFlattening) { if (m_inverseFlattening == DBL_MAX) { return Ellipsoid(name, m_semiMajorAxis, true); } else { return Ellipsoid(name, m_semiMajorAxis, m_semiMajorAxis*(1-1/m_inverseFlattening), m_inverseFlattening, true); } } double Ellipsoid::semiMajorAxis() const { return m_semiMajorAxis; } double Ellipsoid::semiMinorAxis() const { return m_semiMinorAxis; } double Ellipsoid::eccentricity() const { if (m_isSphere) { return 0.0; } const double f = 1-m_semiMinorAxis/m_semiMajorAxis; return sqrt(2*f - f*f); } double Ellipsoid::inverseFlattening() const { return m_inverseFlattening; } bool Ellipsoid::isIvfDefinitive() const { return m_ivfDefinitive; } bool Ellipsoid::isSphere() const { return (m_semiMajorAxis == m_semiMinorAxis); } double Ellipsoid::orthodromicDistance(double x1, double y1, double x2, double y2) { x1 = toRadians(x1); y1 = toRadians(y1); x2 = toRadians(x2); y2 = toRadians(y2); /* * Solution of the geodetic inverse problem after T.Vincenty. * Modified Rainsford's method with Helmert's elliptical terms. * Effective in any azimuth and at any distance short of antipodal. * * Latitudes and longitudes in radians positive North and East. * Forward azimuths at both points returned in radians from North. * * Programmed for CDC-6600 by LCDR L.Pfeifer NGS ROCKVILLE MD 18FEB75 * Modified for IBM SYSTEM 360 by John G.Gergen NGS ROCKVILLE MD 7507 * Ported from Fortran to Java by Martin Desruisseaux. * * Source: ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/inverse.for * subroutine INVER1 */ const int MAX_ITERATIONS = 100; const double EPS = 0.5E-13; const double F = 1/m_inverseFlattening; const double R = 1-F; double tu1 = R * sin(y1) / cos(y1); double tu2 = R * sin(y2) / cos(y2); double cu1 = 1 / sqrt(tu1*tu1 + 1); double cu2 = 1 / sqrt(tu2*tu2 + 1); double su1 = cu1*tu1; double s = cu1*cu2; double baz = s*tu2; double faz = baz*tu1; double x = x2-x1; for (int i = 0 ; i < MAX_ITERATIONS ; ++i) { const double sx = sin(x); const double cx = cos(x); tu1 = cu2*sx; tu2 = baz - su1*cu2*cx; const double sy = sqrt(tu1*tu1 + tu2*tu2); const double cy = s*cx + faz; const double y = atan2(sy, cy); const double SA = s*sx/sy; const double c2a = 1 - SA*SA; double cz = faz+faz; if (c2a > 0) { cz = -cz/c2a + cy; } double e = cz*cz*2 - 1; double c = ((-3*c2a+4)*F+4)*c2a*F/16; double d = x; x = ((e*cy*c+cz)*sy*c+y)*SA; x = (1-c)*x*F + x2-x1; if (fabs(d-x) <= EPS) { if (false) { // 'faz' and 'baz' are forward azimuths at both points. // Since the current API can't returns this result, it // doesn't worth to compute it at this time. faz = atan2(tu1, tu2); baz = atan2(cu1*sx, baz*cx - su1*cu2)+M_PI; } x = sqrt((1/(R*R)-1) * c2a + 1)+1; x = (x-2)/x; c = 1-x; c = (x*x/4 + 1)/c; d = (0.375*x*x - 1)*x; x = e*cy; s = 1-2*e; s = ((((sy*sy*4 - 3)*s*cz*d/6-x)*d/4+cz)*sy*d+y)*c*R*m_semiMajorAxis; return s; } } // No convergence. It may be because coordinate points // are equals or because they are at antipodes. const double LEPS = 1E-10; if ((fabs(x1-x2) <= LEPS) && (fabs(y1-y2) <= LEPS)) { return 0.0; // Coordinate points are equals } if ((fabs(y1) <= LEPS) && (fabs(y2) <= LEPS)) { return fabs(x1-x2) * m_semiMajorAxis; // Points are on the equator. } // Other cases: no solution for this algorithm. return 0.0; } double Ellipsoid::radiusOfCurvature(double latitude) { // WARNING: Code not from geotools double esquare = pow(eccentricity(), 2); return (m_semiMajorAxis * sqrt(1 - esquare) / (1 - esquare * pow( sin(toRadians(latitude)), 2))); } } // namespace Digikam