Layer::fullExtent() cached!?

610
5
12-18-2018 04:47 AM
NorbertThoden
Occasional Contributor III

Hi!
I would like to share an effect around Esri::ArcGISRuntime::Layer::fullExtent():
Context:

I have an application which can toggle from 2DMap to 3Dscene and vice versa.
I call Layer::fullExtent() of all 3 basemapLayers to combine them and get the extent of all basemaps. This i done directly connected to Map/Scene::doneLoading.

Effect:

Sometime the fullExtent (Envelope) is empty or xmin, xmax, ymin and ymax are nan (not a number).
This happens related to the 2nd and 3rd basemapLayer.

Possible cause:
I assume that the extent is cached on first request - even if the layer is not loaded yet.

Workaround:

I skip the call to Layer::fullExtent() if the loadStatus is not loaded yet - works

Maybe someone can have a look into the code!?

Thx

Tags (2)
0 Kudos
5 Replies
LucasDanzinger
Esri Frequent Contributor

Norbert-

Nice to hear from you.

You mention you wait until the Map/Scene are loaded. How about the actual Layer in the basemap layers? Have you ensured that those are loaded before you try to access the fullExtent of them?

0 Kudos
NorbertThoden
Occasional Contributor III

Hi Lucas!

When the first basemap layer is loaded, Map::doneLoading is emitted. Most of my following code depends on that signal.

(The remaining basemaps are loaded simutaneously.)
So it could happen that the code combining the fullExtent of each basemap layer is performed while just the first basemap layer is loaded.

As described above i skip the call to Layer::fullExtent() if the loadStatus is not loaded.

I tried to call fullExtent() on a layer which is not loaded yet. I called fullExtent() a long time after everything is loaded (e.g. 1 minute) and it´s still invalid (nan).
I can´t get correct fullExtent if i performed the call to early!

So i assume that the values are cached even it the layer is not loaded....
Maybe this caching is more like a side effect than itended, but i was surprised about that behaviour.

There is not hint in the documentation and i don´t know, which methods behaves similarly...

0 Kudos
LucasDanzinger
Esri Frequent Contributor
I can´t get correct fullExtent if i performed the call to early!

That is strange behavior indeed...

Can you try modifying your code so that you explicitly wait for doneLoading to emit for all of your Basemap objects before trying to access the extent?

0 Kudos
NorbertThoden
Occasional Contributor III

You suggest to wait for doneLoading of all basemap layer before request fullExtent of each?

I think i already did it with my current workaround, to skip the call if that layer is not loaded yet:

Esri::ArcGISRuntime::Envelope LayerFactoryImpl::fullExtentOfBasemaps(double margin_zeroToOne) const
{
Esri::ArcGISRuntime::LayerListModel *model = d->m_basemap->baseLayers();
if (!model || model->isEmpty())
   return Esri::ArcGISRuntime::Envelope();

double xmin = std::numeric_limits<double>::max();
double ymin = std::numeric_limits<double>::max();
double xmax = std::numeric_limits<double>::lowest();
double ymax = std::numeric_limits<double>::lowest();

Esri::ArcGISRuntime::SpatialReference sr;

for (int idx = 0; idx < model->size(); ++idx)
{
  Esri::ArcGISRuntime::Layer *layer = model->at(idx);
  // 12.12.2018, N. Thoden, V100.4:
  // Do not call fullExtent if layer is not loaded.
  // Envelope contains nan and will not changed on further calls...
  if (!layer || (layer->loadStatus() != Esri::ArcGISRuntime::LoadStatus::Loaded))
    continue;

  const Esri::ArcGISRuntime::Envelope singleEnv = layer->fullExtent();

...

}

...

This avoids getting an invalid fullExtent.

(Typically layer->fullExtent().xmin() ; .ymin() ; xmax() ; ymax() are returning nan if not loaded)

Until now i can´t recognize a relation between the loadStatus of one basemap layer and the result of fullExtent of another basemap layer.

Does this answer your question?

Norbert

BTW:
I wish you a Merry Christmas!

0 Kudos
LucasDanzinger
Esri Frequent Contributor

Hmm, there shouldn't be any relation between the load status of one layer and the full extent of another.

I guess your code will skip out on any that are not loaded or are a nullptr, but ideally you could wait until everything is loaded (or has reached another deterministic state, like failed to load) before you create your union of all the geometries. Similar to the concept of Promises in javascript - not sure what the best way in C++ is to wait until several events fire before you proceed with some block of code.

Maybe something similar to:

Geometry unionedGeom;

for (int i = 0; i < model->size(); ++i)
{
  auto lyr = model->at(i);
  
  if (lyr.loadStatus() == LoadStatus::Loaded)
  {
    unionGeom = GeometryEngine::unionOf(lyr.fullExtent, unionedGeom);
  }

  else
  {
    connect(lyr, &Layer::doneLoading, [this, unionedGeom](Error e)
    {
      unionedGeom = GeometryEngine::unionOf(lyr.fullExtent, unionedGeom);
    });
  }  
}