We experience crashes when using Surface elevationAsync() with a RasterElevationSource when querying outside the extent of the RasterElevationSource.
Our underlying RasterElevationSource data are DTED2 Files of Germany.
We query the elevation on a timer (every 15 ms). We experience no crashes when querying the elevation inside Germany only.
The crash does not happen instantly, it happens suddenly over time. Sometimes it takes a minute for it to crash. Sometimes less, sometimes more. But it always happens after a while.
We are using the most recent ArcGIS Maps SDK 200.6 on Windows 10.
MultipleElevationSources::MultipleElevationSources(QObject* parent /* = nullptr */) : QObject(parent)
{
m_scene = new Scene(this);
QUrl imageLayerUrl("https://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer");
auto baseLayer = new ArcGISMapImageLayer(imageLayerUrl, this);
auto basemap = new Basemap(baseLayer, this);
m_scene->setBasemap(basemap);
m_querySurface = new Surface(parent);
auto queryRasterFilesPaths =
getRasterFilesPaths((defaultDataPath() + "/ArcGIS/Runtime/Data/raster/GermanyElevation").toStdString());
auto queryElevationSource = new RasterElevationSource(queryRasterFilesPaths, m_querySurface);
queryElevationSource->load();
m_querySurface->elevationSources()->append(queryElevationSource);
}
void
MultipleElevationSources::connectSignals()
{
QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this] {
// wkid 4326
// World:
// Using these causes a crash, since queries will be outside data
// x from -180 to 180
// y from -90 to 90
auto x = randomValue(-180.0, 180.0);
auto y = randomValue(-90.0, 90.0);
// Germany
// Using these does not cause a crash, since queries will always be inside data
// x from 11.6 to 14.8
// y from 47 to 54
// auto x = randomValue(11.6, 14.8);
// auto y = randomValue(47, 54);
Point point{x, y, SpatialReference(4326)};
fetchAltitude(point);
});
timer->start(10);
}
void
MultipleElevationSources::fetchAltitude(const Esri::ArcGISRuntime::Point& point)
{
elevationQuery(m_elevationFuture, m_querySurface, point);
}
void
MultipleElevationSources::elevationQuery(QFuture<double>& future, Surface* surface, const Point& point)
{
if (future.isRunning()) {
qDebug() << "Is running...";
return;
}
if (!future.isFinished()) {
//future.cancel();
qDebug() << "Not finished with query yet!";
setAltitude("Waiting");
return;
}
future = surface->elevationAsync(point);
future
.then(
this,
[this](double altitude) {
qDebug() << altitude;
setAltitude(QString::number(altitude));
})
.onCanceled([this] {
qDebug() << "Canceled";
setAltitude("Canceled");
})
.onFailed([this] {
qDebug() << "Failed";
setAltitude("Failed");
});
}
When the crash happens we get the following
Clicking ignore causes the application to crash.
QML Debug Window:
Assertion failed: e && e->get_objectType() == QRTElementType::QRTElementType_float64, file ..\..\qt\common\qt_common\Core\TaskImpl.cpp, line 265
The QML File looks as follows:
import QtQuick
import QtQuick.Controls
import Esri.Samples
Item {
// add a SceneView component
SceneView {
id: view
anchors.fill: parent
Component.onCompleted: {
// Set the focus on SceneView to initially enable keyboard navigation
forceActiveFocus();
}
Text {
text: model.altitude;
font.family: "Helvetica";
font.pointSize: 24;
color: "red";
anchors.bottom: parent.bottom;
anchors.right: parent.right;
anchors.bottomMargin: 20;
}
}
// Declare the C++ instance which creates the map etc. and supply the view
MultipleElevationSourcesSample {
id: model
sceneView: view
}
}
Our use case is querying the elevation on mouse hover. That's when we encountered this crash occasionally.
Find attached the cpp, hpp, qml source file. (Ignore the filename / class name "MultipleElevationSource", that is for another issue, here we only use 1 elevation soruce.)
Hello imbachb,
Thank you for posting this, we’ve confirmed the crash you’re seeing on our end. We've also confirmed that the crash is only possible when the task fails, like you have observed.
To mitigate the crash while we work to fix this, you can perform a couple checks before executing Surface::elevationAsync to ensure it won’t fail. In our testing, a call to elevationAsync can fail if the elevation source isn’t loaded, or if the point you’re querying is out of bounds for that elevation source. So a couple checks before your elevation query could be:
For example:
void
MultipleElevationSources::elevationQuery(QFuture<double>& future, Surface* surface, const Point& point)
{
auto* elevationSource = surface->elevationSources()->first();
if (elevationSource->loadStatus() != LoadStatus::Loaded)
{
// Early return logic
}
auto fullExtent = elevationSource->fullExtent();
if (!GeometryEngine::within(fullExtent, point))
{
// Early return logic
}
// Continue with the elevationAsync query...
While we cannot guarantee at the moment what release version will contain the fix, we are continuing to work on this internally. Thank you again for bringing this to our attention.