Files
qt-migration-legatus/qt5-before/dlbo/Lts/Viewer/SituationMap/MapImpl/tms_layer.cpp
T
torstenandCopilot 7173785915 Qt6 Migration: Complete migration with DDS/RTI support and build validation
Major Changes:
- Merged latest Qt5 devel content from dlbo (292 files changed, 7 new dirs)
- Consolidated forward-declarations and include folders
- Removed unneeded modules per Niklas' repo check list

Build System Fixes:
- Fixed CMake VERSION_GREATER syntax in ParticipantManagement/CMakeLists.txt
- Added VLC packages (libvlc-dev, vlc-plugin-base) to docker_build.sh
- Added PostgreSQL dev packages (libpq-dev, postgresql-server-dev-all) to Dockerfile
- Fixed locale warnings (ANSI_X3.4-1968 → C.UTF-8)
- Fixed ArcGIS SDK path warnings when BUILD_ARCGIS=OFF

Code Generation Fixes:
- Fixed IDL enum collisions (PortType→CorePortType/DdsutPortType, PdmEventType)
- Fixed Grantlee template svc_translation.cpp.grantlee:
  - Changed {{ idl_enum.name }}_def:: to {{ idl_enum.name }}::
  - Fixed .underlying() calls for C++11 scoped enums (enum class)
- Removed generated files from repo (moc_*, *_dtc.h, *_ftc.h)

Validation:
- BUILD_RTI=ON: SUCCESS
- BUILD_RTI=OFF: SUCCESS
- C++17 standard maintained for RTI 7.7 compatibility
- Ubuntu 26.04 Docker environment verified

Remaining Warnings (expected):
- BgViewer, KDChart, KDReports, Poppler-Qt6 not found (optional features)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-15 18:20:26 +00:00

214 lines
7.0 KiB
C++

/**
* @RDE_HEADER@
* Tile Map Service Layer for Esri
* $Id$
* $HeadURL$
* $Author$
* License: BSD 2-Clause
*/
#include "tms_layer.h"
#include <QCoreApplication>
#include <QDomDocument>
#include <QNetworkAccessManager>
namespace EsriExtension
{
TMSLayer::TMSLayer(const QString &url, QObject *parent)
: EsriRuntimeQt::TiledMapServiceLayer(url, parent)
, m_nam(nullptr)
{
connect(this, SIGNAL(requestTile(int,int,int)),
this, SLOT(onRequestTMSTile(int,int,int)));
}
TMSLayer::~TMSLayer()
{
delete m_nam;
}
void TMSLayer::initialize()
{
if (!m_nam)
{
m_nam = new QNetworkAccessManager(this);
connect(m_nam, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyBaseXmlFinished(QNetworkReply*)));
}
QNetworkReply *reply = m_nam->get(QNetworkRequest(QUrl(url())));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(replyBaseXmlError(QNetworkReply::NetworkError)));
}
void TMSLayer::replyBaseXmlFinished(QNetworkReply *reply)
{
if (!reply || reply->error() != QNetworkReply::NoError)
{
cleanupNetworkReply(reply);
return;
}
QDomDocument doc(QLatin1String("TMSLayerDoc"));
doc.setContent(reply);
const QDomElement domRoot = doc.documentElement();
const QDomElement domSRS = domRoot.firstChildElement(QLatin1String("SRS"));
const QDomElement domBoundingBox = domRoot.firstChildElement(QLatin1String("BoundingBox"));
const QDomElement domOrigin = domRoot.firstChildElement(QLatin1String("Origin"));
const QDomElement domTileFormat = domRoot.firstChildElement(QLatin1String("TileFormat"));
const QDomElement domTileSets = domRoot.firstChildElement(QLatin1String("TileSets"));
if (domSRS.isNull() ||
domBoundingBox.isNull() ||
domOrigin.isNull() ||
domTileFormat.isNull() ||
domTileSets.isNull())
{
cleanupNetworkReply(reply);
const QString errStr(QString(QLatin1String("Got invalid xml from tms server (url: %1)")).arg(url()));
layerCreateError(errStr);
qWarning("%s", qPrintable(errStr));
return;
}
// optional
if (domTileFormat.hasAttribute(QLatin1String("extension")))
m_extension = domTileFormat.attribute(QLatin1String("extension"));
//LODs:
const double dpi = 90.7; // according to spec
const double ipf = 12.0; // inch per foot
const double dpu = dpi * ipf; // dots per unit
// We expect a SRS string in the format 'EPSG:<number>' here ...
const EsriRuntimeQt::SpatialReference sr(domSRS.text().split(QLatin1Char(':')).at(1).toInt());
// Setting the image format is not needed (the image reader correctly guesses the format)
// Setting the spatial reference is also not needed - if not set, the default one
// from the layer is used.
EsriRuntimeQt::TileInfo tileInfo;
tileInfo.setCols(domTileFormat.attribute(QLatin1String("width")).toInt());
tileInfo.setRows(domTileFormat.attribute(QLatin1String("height")).toInt());
tileInfo.setDpi(dpi);
EsriRuntimeQt::LOD lod;
QList<EsriRuntimeQt::LOD> lods;
const double minX = domBoundingBox.attribute(QLatin1String("minx")).toDouble();
const double minY = domBoundingBox.attribute(QLatin1String("miny")).toDouble();
const double maxX = domBoundingBox.attribute(QLatin1String("maxx")).toDouble();
const double maxY = domBoundingBox.attribute(QLatin1String("maxy")).toDouble();
// Since ArcGIS calculates from upper left (instead lower left as tms defines),
// we need to make sure all our zoom levels have the same upper y value
// TODO: use from zoom level
const double utm32MaxYcalc = 16384 * 256 * 3; // 3 - Anzahl kacheln lowest zoom level
const double utm32MinX = 166021.44309607695; // min utm 32 boundary - calc from proj4 or smth. like this
const QString tsAttr(QLatin1String("TileSet"));
QDomElement tileSetSibling = domTileSets.firstChildElement(tsAttr);
for (; !tileSetSibling.isNull(); tileSetSibling = tileSetSibling.nextSiblingElement(tsAttr))
{
if (!tileSetSibling.hasAttribute(QLatin1String("order")))
{
qWarning("Could not find 'order' attribute for LOD - skipping lod");
continue;
}
const int lodLevel = tileSetSibling.attribute(QLatin1String("order")).toInt();
lod.setLevel(lodLevel);
lod.setResolution(tileSetSibling.attribute(QLatin1String("units-per-pixel")).toDouble());
lod.setScale(lod.resolution() / dpu);
if (!tileSetSibling.hasAttribute(QLatin1String("href")))
{
qWarning("Could not find 'href' attribute for LOD - skipping lod");
continue;
}
lods.append(lod);
m_tileUrls.insert(lodLevel, tileSetSibling.attribute(QLatin1String("href")));
// Since ArcGIS calculates from upper left (instead lower left as tms defines),
// we need to make sure all our zoom levels have the same upper y value
m_tilesYAxis.insert(lodLevel, qRound(utm32MaxYcalc / (lod.resolution() * tileInfo.cols())));
//qWarning("LOD: %d, %lf %lf, %lf", lodLevel, lod.resolution(), lod.scale(),
// lod.resolution() * 256 * m_tilesYAxis.value(lodLevel));
//qWarning("Y Tile count for level %d is %d - %lf", lodLevel, m_tilesYAxis.value(lodLevel),
// utm32MaxYcalc / (lod.resolution() * tileInfo.cols()));
}
if (lods.isEmpty())
{
const QString errStr(QLatin1String("Could not find any layer in the xml file!"));
layerCreateError(errStr);
qWarning("%s", qPrintable(errStr));
cleanupNetworkReply(reply);
return;
}
tileInfo.setLODs(lods);
const EsriRuntimeQt::Envelope envelope(minX, minY, maxX, maxY, sr);
// TMS service origin is bottom left, arcgis upper left
tileInfo.setOrigin({utm32MinX, utm32MaxYcalc, sr});
setDefaultSpatialReference(sr);
setFullExtent(envelope);
setInitialExtent(envelope);
setTileInfo(tileInfo);
// TODO: setMin/Max scale ?
EsriRuntimeQt::TiledMapServiceLayer::initialize();
// clean up
cleanupNetworkReply(reply);
}
void TMSLayer::replyBaseXmlError(QNetworkReply::NetworkError error)
{
const QString errStr(QString(QLatin1String("Error retrieving xml from %1, error: %2")).arg(url()).arg(error));
emit layerCreateError(errStr);
qWarning("%s", qPrintable(errStr));
}
void TMSLayer::onRequestTMSTile(int level, int col, int row)
{
if (!m_tilesYAxis.contains(level))
{
qWarning("Invalud zoom level (%d)!", level);
return;
}
// TMS starts counting from bottom left, ArcGIS from upper left
// -> we need to mirror the y-axis
int reqRow = m_tilesYAxis.value(level) - row - 1;
//qWarning("onRequestTMSTile: %d, %d, %d (%d)", level, col, reqRow, row);
QString requestUrl(m_tileUrls.value(level) +
QLatin1Char('/') + QString::number(col) +
QLatin1Char('/') + QString::number(reqRow));
if (!m_extension.isEmpty())
requestUrl += QLatin1Char('.') + m_extension;
//qWarning("RequestUrl: %s", qPrintable(requestUrl));
setTile(level, col, row, QUrl(requestUrl));
}
void TMSLayer::cleanupNetworkReply(QNetworkReply *reply)
{
if (reply)
{
reply->deleteLater();
reply->disconnect(this);
}
if (m_nam)
{
m_nam->disconnect(this);
m_nam->deleteLater();
m_nam = nullptr;
}
}
} // namespace EsriExtension