Cache map tiles locally server-side #827
Closed
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.
This PR attempts to implement a file-based local server-side cache for map tiles, addressing some of the concerns of #714
Motivation
Together with #826 this PR allows to run local server without internet connection. When reliable connection is available, user may selectively visit regions of interest on the map in order to build up the cache, and then go offline, with these same regions still being available. Because cache is filesystem-based, offline tiles persist between server restarts and browser sessions.
Implementation details
In order to not violate terms of currently used MapBox service, this PR switches to OpenStreetMap provider, which provides free tiles and allows (even encourages) caching.
Instead of fetching tiles directly, leaflet layer now requests tiles from
/tiles/{s}/{x}/{y}/{z}
(when running server locally, this resolves to localhost). On server side, this request is routed toTileCache
service that initially proxies requests to the remote tile provider of choice, currently OpenStreetMap.The image tiles resulting from this request are sent back to client, but are also asynchronously written to filesystem. If previously seen tile is requested again,
TileCache
will return corresponding cached file instead of making requests to remote tile provider.When remote tiles cannot be successfully retrieved,
TileCache
currently returns status 404. Leaflet in this case renders a grey tile.Discussion and further improvements
OpenStreetMap Tile Usage Policy allows caching and proxying, but they forbid setting HTTP user agent. However, requesting tiles with
node-fetch
always returns status 429, therefore, in this PR we forwardUser-Agent
from the original client (browser) request. We have to check if this behavior is allowed.Although individual tile images are small (10-20kb), they come in bulk numbers and overall cache size may reach limits of available disk space. We may implement various cache eviction policies (e.g. LRU) to free up disk space on user's machine.
Similarly, caching can be implemented client-side. The tiles can be stored in LocalStorage or in IndexedDB. This is more tricky, as it will involve modifying leaflet's layer, but is doable.
Currently, remote tiles are being requested, stored in a buffer and then sent to client/written to disk. Instead, for performance reasons, we may want to pipe the response data stream from tile provider directly to the response stream. This however means that we need to "fork" the very same stream in order to be able to also pipe it to file stream.
When a remote tile cannot be fetched, currently we return 404 and leaflet renders its grey tile. We could easily send another PNG image instead, to distinguish "in-progress" grey tiles and "failed" tiles.
The application can come pre-bundled with tiles up to a certain zoom level.
We may want to use any of the alternative map styles or design our own. See openmaptiles.org/styles.