Previously, we could first get a stale response from cache, then immediately a "connection error" response that overwrote the error state variable. In the tileLoadingCompleteCallback, we'd only see the second overwritten error state and abort. This could lead to the map sometimes not being rendered at all until the user interacts with the map.
This fixes adding shape annotations after VectorTileData objects have
been created for annotations already, and will also be necessary for
the dynamic Style API.
To encapsulate the differences, this introduces an abstract
GeometryTileMonitor class, with concrete VectorTileMonitor and
AnnotationTileMonitor implementations, and makes pbf parsing for
VectorTile data happen lazily in getLayer, since we want that to
happen on the worker thread, rather than when VectorTile is created.
Fixes#2792
We're now reparsing tiles when they expire. We're also swapping out buckets atomically to avoid flickering data; i.e. we're displaying the old data as long as we don't have a new parsed bucket for that layer yet. The parsed buckets now live in the *TileData objects rather than in the TileWorker; only partially parsed == pending buckets will remain in the TileWorker. Once they're parsed, they're moved to the *TileData object.
Annotation tiles may become partially parsed just like regular tiles,
for example if a point annotation is added to the map before the style's
sprite has been loaded. In such cases, they need to be reparsed or the
annotation will not be rendered. Previously, the code path for reparsing
would be short-circuited by a dynamic_cast<VectorTileData*> followed by
a null check. This commit removes that case and adds (back) a virtual
reparse method to the TileData interface.
This is needed because of a race that can occur now that we
requests Glyphs from the Map Thread:
The glyph might arrive at the moment a tile is being parsed
and they won't be notified again of any new data, remaining
forever at the partial state.
Layers are added and removed dynamically on the Map thread when we use shape
annotations and we are iterating style.layers on the Worker thread without
any lock.
Now when we create a [Live|Vector]TileData at the Map thread we give it
a layers list (instead of the Style) and [Live|Vector]TileData will ultimately
make a copy of this list (on the Map thread, so no concurrency with
adding shape annotations).
The copy should be relatively cheap because we are using a shared pointer
for storing the layers on the list.
We only emit signals when the number of buckets increase (i.e. the
new data that arrived added a new bucket to the tile).
Also, if we try to reparse a partial tile but it is not possible, we
keep the flag up for trying to reparse it again on the next update,
because the data might have arrived after the bucket that is waiting
for the data was processed but before the end of the parsing.
Fixes#1458
We keep the Collision object alive longer because we need it
for the SymbolBuckets yet to be created in case more data
resources arrive. When the tile is no longer needed or if it
is completely parsed or obsolete (i.e. in a immutable state)
we throw the Collision object away.
This change will add a new state to the tile called "partial" which
is a state where the tile can be rendered but some information might
be missing.
For now we skip the SymbolBucket if the Sprites are not loaded (will
also do the same for the glyphs in the future). When the Sprites are
finally loaded, we trigger another update, this time the Tile will
get completely re-rendered and change the state to "parsed".
We are also removing the code that is passing the Environment around
and instead, we get now the Environment in the TileData using
Environment::Get().
When processing the job, the work will be temporally registered to
the Environment as "TileWorker_0/0/0" which can be used for logging
in the future. At the end of the job, it gets unregistered
automatically from the Environment.
we are now scoping all file requests to an environment object. The FileSource implementation treats
this as an opaque pointer, but allows canceling all Requests that are associated with that pointer.
This is necessary to abort all file requests that originated from a particular Map object. Aborting
a file request is different from canceling a file request: A canceled request doesn't have its
callback called, while an aborted request will have its callback called with an error, indicating
that the environment is going to be shut down.