<div dir="ltr"><div>Thanks, I was looking in OL internals when this could be done with public API. Your code was almost right, I only had to recalculate extent used to read features to account for the larger tile. I will need to investigate how this affects z-ordering with complex styles, but this works for the moment. <br></div><div><br></div><div>Is there any reason why the loader should be synchronous?<br></div><div> <br></div><div>Here's an async TS version that worked for me, if somebody else needs it:</div><div><br></div><div>const recursivelyFetchTile = (url, projection, tileGrid, tileUrl, center, curLevel) => new Promise((resolve, reject) => {<br> fetch(url).then((response) => {<br> if (response.status !== 200) {<br> if (curLevel > tileGrid.getMinZoom()) {<br> const ntileCoord = tileGrid.getTileCoordForCoordAndZ(center, curLevel - 1 );<br> resolve(recursivelyFetchTile(tileUrl(ntileCoord, devicePixelRatio, projection),<br> projection, tileGrid, tileUrl, center, curLevel - 1));<br> }<br> else<br> reject('NONE');<br> }<br> else<br> resolve(response.arrayBuffer().then((data) => [ data,<br> tileGrid.getTileCoordExtent(tileGrid.getTileCoordForCoordAndZ(<br> center, curLevel))<br> ]));<br> })<br> });</div><div><br></div><div>tileLoadFunction: (tile, url) => {<br> (tile as VectorTile).setLoader((extent, resolution, projection) => {<br> const tileGrid = vtSource.getTileGrid();<br> const tileCoord = tile.getTileCoord();<br> const zoomLevel = tileCoord[0];<br> const center = getCenter(tileGrid.getTileCoordExtent(tile.getTileCoord()));<br> const tileUrl = vtSource.getTileUrlFunction();<br> const format = (tile as VectorTile).getFormat()<br><br> recursivelyFetchTile(url, projection, tileGrid, tileUrl, center, zoomLevel)<br> .then(([data, extent]) => { <br> const features = format.readFeatures(data, {<br> extent: extent,<br> featureProjection: projection<br> });<br> (tile as VectorTile).setFeatures(features as Feature[]);<br> }).catch((reason) => {<br> console.log('rejected', reason);<br> (tile as VectorTile).setFeatures([]);<br> })<br> });<br> <br> }</div><div><br></div><div>Regards,</div><div>Daniel Urda<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Aug 4, 2020 at 5:01 PM Andreas Hocevar <<a href="mailto:andreas.hocevar@gmail.com">andreas.hocevar@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div dir="ltr">The easiest way to achieve this is to configure a custom tileLoadFunction for the VectorTile source. That function, when it receives a 404, has to request the tile for the next lower zoom, until it receives a 200, and call `setFeatures` with the result. Something like this (untested):<div><br></div><div><div style="line-height:18px"><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"><span style="color:rgb(175,0,219)">import</span> {<span style="color:rgb(0,16,128)">getCenter</span>} <span style="color:rgb(175,0,219)">from</span> <span style="color:rgb(163,21,21)">'ol/extent'</span>;</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"><span style="color:rgb(175,0,219)">import</span> <span style="color:rgb(0,16,128)">MVT</span> <span style="color:rgb(175,0,219)">from</span> <span style="color:rgb(163,21,21)">'ol/format/MVT'</span>;</div><br><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"><span style="color:rgb(0,0,255)">const</span> <span style="color:rgb(0,112,193)">source</span> = <span style="color:rgb(0,0,255)">new</span> <span style="color:rgb(38,127,153)">VectorTileSource</span>({</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">format:</span> <span style="color:rgb(0,0,255)">new</span> <span style="color:rgb(38,127,153)">MVT</span>(),</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">url:</span> <span style="color:rgb(163,21,21)">'<a href="https://my.tiles/%7Bz%7D/%7Bx%7D/%7By%7D.pbf" target="_blank">https://my.tiles/{z}/{x}/{y}.pbf</a>'</span>,</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(121,94,38)">tileLoadFunction</span>(<span style="color:rgb(0,16,128)">tile</span>, <span style="color:rgb(0,16,128)">url</span>) {</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">tile</span>.<span style="color:rgb(121,94,38)">setLoader</span>(<span style="color:rgb(0,0,255)">async</span> (<span style="color:rgb(0,16,128)">extent</span>, <span style="color:rgb(0,16,128)">resolution</span>, <span style="color:rgb(0,16,128)">projection</span>) <span style="color:rgb(0,0,255)">=></span> {</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">let</span> <span style="color:rgb(0,16,128)">response</span> = <span style="color:rgb(175,0,219)">await</span> <span style="color:rgb(121,94,38)">fetch</span>(<span style="color:rgb(0,16,128)">url</span>);</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">let</span> <span style="color:rgb(0,16,128)">status</span> = <span style="color:rgb(0,16,128)">response</span>.<span style="color:rgb(0,16,128)">status</span>;</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">let</span> <span style="color:rgb(0,16,128)">tileCoord</span> = <span style="color:rgb(0,16,128)">tile</span>.<span style="color:rgb(0,16,128)">tileCoord</span>;</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">const</span> <span style="color:rgb(0,112,193)">tileGrid</span> = <span style="color:rgb(0,16,128)">source</span>.<span style="color:rgb(121,94,38)">getTileGrid</span>();</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(175,0,219)">while</span> (<span style="color:rgb(0,16,128)">status</span> !== <span style="color:rgb(9,134,88)">200</span> && <span style="color:rgb(0,16,128)">tileCoord</span>[<span style="color:rgb(9,134,88)">0</span>] > <span style="color:rgb(0,16,128)">tileGrid</span>.<span style="color:rgb(121,94,38)">getMinZoom</span>()) {</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">const</span> <span style="color:rgb(0,112,193)">z</span> = <span style="color:rgb(0,16,128)">tileCoord</span>[<span style="color:rgb(9,134,88)">0</span>];</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">tileCoord</span> = <span style="color:rgb(0,16,128)">tileGrid</span>.<span style="color:rgb(121,94,38)">getTileCoordForCoordAndZ</span>(</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(121,94,38)">getCenter</span>(<span style="color:rgb(0,16,128)">tileGrid</span>.<span style="color:rgb(121,94,38)">getTileCoordExtent</span>(<span style="color:rgb(0,16,128)">tileCoord</span>)), <span style="color:rgb(0,16,128)">z</span> - <span style="color:rgb(9,134,88)">1</span></div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> );</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">response</span> = <span style="color:rgb(175,0,219)">await</span> <span style="color:rgb(121,94,38)">fetch</span>(<span style="color:rgb(0,16,128)">source</span>.<span style="color:rgb(121,94,38)">getTileUrlFunction</span>()(<span style="color:rgb(0,16,128)">tileCoord</span>, <span style="color:rgb(0,16,128)">devicePixelRatio</span>, <span style="color:rgb(0,16,128)">projection</span>));</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> }</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">const</span> <span style="color:rgb(0,112,193)">data</span> = <span style="color:rgb(175,0,219)">await</span> <span style="color:rgb(0,16,128)">response</span>.<span style="color:rgb(121,94,38)">arrayBuffer</span>();</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">const</span> <span style="color:rgb(0,112,193)">format</span> = <span style="color:rgb(0,16,128)">tile</span>.<span style="color:rgb(121,94,38)">getFormat</span>()</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,0,255)">const</span> <span style="color:rgb(0,112,193)">features</span> = <span style="color:rgb(0,16,128)">format</span>.<span style="color:rgb(121,94,38)">readFeatures</span>(<span style="color:rgb(0,16,128)">data</span>, {</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">extent:</span> <span style="color:rgb(0,16,128)">extent</span>,</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">featureProjection:</span> <span style="color:rgb(0,16,128)">projection</span></div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> });</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> <span style="color:rgb(0,16,128)">tile</span>.<span style="color:rgb(121,94,38)">setFeatures</span>(<span style="color:rgb(0,16,128)">features</span>);</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> });</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap"> }</div><div style="color:rgb(0,0,0);font-family:Menlo,Monaco,"Courier New",monospace;font-size:12px;white-space:pre-wrap">});</div><br>I hope this helps.<br><br>Andreas.</div></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Aug 4, 2020 at 2:45 PM Daniel Urda <<a href="mailto:daniel.urda.ct@gmail.com" target="_blank">daniel.urda.ct@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">
<div>Hello,</div><div><br></div><div>I am resending this since the mail requiring confirmation of registration to the mailing list went to my spam folder. Sorry if this results in a duplicate mail.<br></div><div><br></div><div>I am trying to consume a vector tiles as provide by an ArcGIS Enterprise Server. I successfully managed to hijack the MapboxVector layer in order to display the tiles in OpenLayers (fortunately style description is more or less compatible with MapboxVector). <br></div><div>The issue is that ESRI Vector Tile Services randomly decide to drop lower level tiles. Thus, if ESRI somehow decides that for a certain area level X+1 does not provide significantly more information than level X, tiles on level X+1 are not generated
(requests result in 404)
and the client is expected to display the data on level X. ArcGIS JS does have a mechanism to make sure it never gets 404 (making use of <a href="https://developers.arcgis.com/rest/services-reference/tile-map.htm" target="_blank">https://developers.arcgis.com/rest/services-reference/tile-map.htm</a> ), however it seems to me that implementing that in OL would need significant effort with low benefits. Setting maxZoom on the vector tile is not really helpful, since better zoom levels may exist in some regions (detecting the max level that has tiles for all areas is cumbersome, but doable using the API in the provided link). <br></div><div>Therefore, I was thinking I could somehow convince OL to render the tile at best available resolution when it gets 404s at better resolution. Any hint to where exactly in the code I could do that?</div><div><br></div><div>Thanks,</div><div>Daniel Urda</div>
</div>
_______________________________________________<br>
Users mailing list<br>
<a href="mailto:Users@lists.osgeo.org" target="_blank">Users@lists.osgeo.org</a><br>
<a href="https://lists.osgeo.org/mailman/listinfo/openlayers-users" rel="noreferrer" target="_blank">https://lists.osgeo.org/mailman/listinfo/openlayers-users</a></blockquote></div>
</blockquote></div>