Repository navigation
Stack ArcGIS graphics by zIndex within a band, whatever order they arrive in - #28
Merged
Merged
Conversation
…rive in Within the polygon, circle, polyline and marker bands, a zIndex=1 overlay added before a zIndex=0 one ended up underneath it. Measured on the tablet (stackctl page, zIndex=1 declared first): - Polygons: right on 3D, wrong on 2D. The renderer wrote zIndex into an attribute and re-sorted the overlay's collection (clear + addAll) in onPostProcess. The collection did end up sorted (logged), but MapView kept drawing in the original order; only SceneView follows the collection order. - Circles and polylines: wrong on both, because nothing read zIndex. The marker renderer ignored it too (seen in the code and the test; the check page has no overlapping markers). ArcGIS draws a GraphicsOverlay by each Graphic's zIndex property, ties in the order they were added, on both SceneView and MapView. Every renderer now sets that property on create and on update (ArcGISGraphicsZIndex), and the polygon renderer no longer re-sorts the collection: with the property set, 3D and 2D both draw right without it, and clear + addAll could put back a graphic an asynchronous removal had just taken out. The polyline renderer touched GraphicsOverlay.graphics on Dispatchers.Default. Removing two polylines at once made the two removals race and crashed the app with ConcurrentModificationException (seen on develop as soon as the check page swapped two polylines). It now touches the graphics on the main thread like the polygon and circle renderers, and only interpolates the vertices on Default. The ground image and raster bands were already ordered: ground images carry no zIndex on Android, and raster layers go through ArcGISOverlayStacking with theirs. ArcGISGraphicsZIndexTest (instrumented) adds a zIndex=1 overlay first and a zIndex=0 one after for each kind, and checks the order ArcGIS draws them in; all five tests failed before and pass now.
The ground image renderer added and removed its WebTiledLayer in the map's operational layers from Dispatchers.Default, while the raster renderer and the marker tiles change the same list on the main thread. Taking a ground image and a raster layer away together (the stackctl check page does this when it re-sends every overlay) crashed the app on the tablet with ConcurrentModificationException in ArcGISRasterLayerOverlayRenderer.removeLayer. It showed up once the polyline crash in front of it was fixed. The list is now only touched on the main thread; building the tiles and loading the layer stay on Default. ArcGISGroundImageLayerThreadTest (instrumented) records which thread adds and removes the layer; it failed before (both on DefaultDispatcher-worker) and passes now.
Writing the state's zIndex into Graphic.zIndex fixed zIndex=1 drawing under zIndex=0, but ties were left to ArcGIS, which does not keep the order graphics were added (its reference says they "usually" render in that order and to set zIndex explicitly when order matters). On the tablet in 2D: - two zIndex=0 polygons with a zIndex=1 one added between them drew the later one underneath (it was right on develop, where nothing set Graphic.zIndex); - markers with equal zIndex drew in neither order, on develop as well. Each GraphicsOverlay now keeps a stack order (ArcGISGraphicsZIndex): graphics sorted by (state zIndex, arrival), with that rank written to Graphic.zIndex, so no two graphics share a value. A zIndex-only update moves the graphic but keeps its place among equals; a graphic taken off and added again goes on top of its zIndex. Ranks only need to increase, so removals leave gaps and adding on top writes one value. ArcGISGraphicsZIndexTest gets three tests (equal zIndex polygons around a higher one, equal zIndex markers, zIndex change and re-add on circles); they failed on the previous commit and pass now. The stackctl check page shows every band right with zIndex=1 added first or last, after a zIndex swap, same-id new states, partial removal and restore, clearOverlays and re-send, and a design change.
Compose cancels the running LaunchedEffect whenever an overlay's states change. The ArcGIS renderers put graphics and layers on the map in their own scope (main thread or Default) while the caller waits on the compose dispatcher; cancelled in between, withContext throws away the result, so the graphic stays on the map but is never registered. The next sync adds the same id again, and the first copy is never taken off -- clearOverlays leaves it too. Seen on the tablet in 2D: right after start, markers, polygons and polylines intermittently came in twice per id (1 of 4 launches of the check page; a run that hit it kept 5 extra marker graphics through clearOverlays), and polylines added and removed in quick succession were left behind. This, not the clear() + addAll() re-sort, is where the duplicate polygons seen earlier came from. The ArcGIS controllers now run add() to the end (NonCancellable); the newer states that caused the cancellation are applied by the next sync, which waits behind the semaphore. Removal already tolerated cancellation (it removes from the map before forgetting the entity). ArcGISOverlaySyncCancellationTest (instrumented) cancels an add while the main thread is busy and then syncs again, for polygons, polylines, circles and markers; all four found two graphics before and one now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
なぜ
ArcGIS の 2D(MapView)で、同じ帯(ポリゴン・サークル・ポリライン・マーカー)の中の zIndex が、オーバーレイを足した順に左右されていました。zIndex=1 を先に、zIndex=0 を後に足すと、zIndex=1 が下に描かれます。タブレットで確かめた内訳:
onPostProcessでコレクションを並べ替えていました(clear + addAll)。コレクションは並んでいましたが、MapView は最初に足した順のまま描きます(コレクション順に従うのは SceneView だけ)。さらに、ArcGIS は同じ zIndex の Graphic を足した順に描くとは保証していません(API リファレンスは「通常は」足した順で、順が大事なら zIndex を明示するように、と書いています)。実際タブレットの 2D で、zIndex=0 のポリゴン 2 つの間に zIndex=1 を足すと後の zIndex=0 が下になり(develop では正しかった)、同じ zIndex のマーカーはどちらの順にもなりませんでした(develop でも)。
確かめる途中で、次の 3 つの落ち方・ずれも見つけたので一緒に直しています。
ConcurrentModificationExceptionで落ちる(develop で毎回再現)。ポリラインの renderer がGraphicsOverlay.graphicsをDispatchers.Defaultから触っていました。ConcurrentModificationExceptionで落ちる(1. を直すと見えるようになりました)。GroundImage の renderer が operational layers をDispatchers.Defaultから触り、ラスターとマーカータイルはメインスレッドから触っていました。LaunchedEffectを取り消しますが、renderer は自分のスコープで地図に Graphic を載せるので、その間に取り消されるとwithContextが結果を捨て、Graphic は地図に残ったまま entity に登録されません。次の同期で同じ id をもう一度足し、最初の 1 つは外されずclearOverlaysでも残ります。以前見えていたポリゴンの重複もこれが原因でした。何を変えたか
ArcGISGraphicsZIndex)を持ち、(state の zIndex, 渡された順) で並べた順位をGraphic.zIndexに書きます。同じ値の Graphic は 2 つできません。zIndex だけの更新は Graphic を動かしても同じ zIndex の中での位置を保ち、外して戻したものはその zIndex の一番上に載ります。順位は増えるだけでよいので、外したところは空きのままにし、上に足すときは 1 つ書くだけです。Defaultに残しました(ポリゴン・サークルと同じ形)。Defaultに残しました。add()をNonCancellableで最後まで走らせます。取り消しの元になった新しい state は、semaphore の後ろで待っている次の同期が反映します。削除はもともと取り消しに耐えます(地図から外してから entity を忘れる)。GroundImage とラスターの帯はもともと順が正しかった(Android の GroundImage は zIndex を持たず、ラスターは
ArcGISOverlayStackingを通る)ので変えていません。公開 API:
ArcGISGraphicsZIndexの追加のみです(apiCheckのベースラインを更新)。検証
ArcGISGraphicsZIndexTest(7 件): ポリゴン・サークル・ポリライン・マーカーそれぞれで zIndex=1 を先・zIndex=0 を後に足し ArcGIS が描く順を確かめる 4 件、同じ zIndex のポリゴンの間に高いものを足す・同じ zIndex のマーカー・サークルの zIndex 変更と足し直しの 3 件。ArcGISGroundImageLayerThreadTest(1 件): レイヤーを足す・外すスレッドを記録。直す前は両方 DefaultDispatcher-worker でした。ArcGISOverlaySyncCancellationTest(4 件): メインスレッドを塞いだ状態で add を取り消し、もう一度同期する。ポリゴン・ポリライン・サークル・マーカーとも、直す前は Graphic が 2 つ、直した後は 1 つ。clearOverlaysしてから送り直し、デザイン変更。確認できていないもの
関連: iOS 側 MapConductor/ios-for-arcgis#18