Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/sfomuseum/swift-mbtiles
Swift package for reading and caching data from MBTile databases.
https://github.com/sfomuseum/swift-mbtiles
mbtiles sqlite swift
Last synced: 9 days ago
JSON representation
Swift package for reading and caching data from MBTile databases.
- Host: GitHub
- URL: https://github.com/sfomuseum/swift-mbtiles
- Owner: sfomuseum
- License: other
- Created: 2020-11-16T20:51:00.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2024-06-11T20:08:26.000Z (5 months ago)
- Last Synced: 2024-06-12T04:51:40.072Z (5 months ago)
- Topics: mbtiles, sqlite, swift
- Language: Swift
- Homepage:
- Size: 890 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# swift-mbtiles
Swift package for reading and caching data from MBTile databases.
## Important
Work in progress, including documentation.
## Example
```
// Logger is part of the swift-log packacge and is an optional parameter for
// the swift-mbtiles classes described belowlet logger = Logger(label: "org.example.mbtiles")
logger.logLevel = .infolet tiles_resolver = TileResolver()
let root = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let tiles_collection = MBTilesCollection(root: root, logger: logger)
let tiles_pool = MBTilesDatabasePool(logger: logger)
let tiles_reader = MBTilesReader(resolver: tiles_resolver, logger: logger)
let tiles_cache = MBTilesCache(db_pool: tiles_pool, db_reader: tiles_reader, resolver: tiles_resolver, throttle: 10, logger: logger)
```An attempt has been made to create small and discrete classes that only do a finite set of tasks. They are:
* `MBTilesCollection` This class manages a set of MBTiles database contained in a parent directory.
* `MBTilesDatabasePool` This class manages database connections to one or more MBTiles (SQLite) databases.
* `MBTilesReader` This class manages queries of and reading data from an MBTiles (SQLite) database.
* `MBTilesCache` This class manages a caching layer for tile requests. Interface-wise it's a bit of a mess; this is discussed more below.Some of these classes suffer, in a technical sense, from "leaky abstractions". That's not ideal but in the interest of "getting things done" they are understood as acceptable compromises until such a time as they are not.
Under the hood this package is using [stephencelis/SQLite.swift](https://github.com/stephencelis/SQLite.swift) for database access. The (SQLite) database layer has not been abstracted behind a single class (`MBTilesDatabasePool`). The different `swift-mbtiles` classes pass each other `SQLite` instances. This is one of those places where, in time, we might be able to develop a higer level abstraction for MBTiles-related tasks but, as of this writing, it's still too soon for that.
There is also [a branch that uses FMDB](https://github.com/sfomuseum/swift-mbtiles/tree/fmdb) but it contains a crashing bug that I haven't been able to debug (taking in to account all the things that the documentation says to do).
Did you notice the instatiation of the `TileResolver()` class above? This is code that you will need to implement and that conforms to the `MBTilesResolver` protocol below. This is code used to resolve a URL in to MBTile database specifics like the name of the database and Z, X, Y coordinates.
Once all of these classes have been instantiated you can precache the tiles in your MBTiles databases like this:
```
DispatchQueue.global(qos: .background).async {
let db_rsp = tiles_collection.Databases()
var database_urls = Array()
switch db_rsp {
case .failure(let error):
// handle error here
case .success(let urls):
database_urls = urls
}
let cache_rsp = tiles_cache.PrecacheTileData(databases: database_urls)
if case .failure(let error) = cache_rsp {
// handle error here
}
}
```And then later on in your code when a tile is requested we check to see if we have a cached version:
```
if let _ = tiles_cache.missing.object(forKey: tile_path as NSString) {
return .failure(Errors.missingTileError)
}
if let tile_data = tiles_cache.cache.object(forKey: tile_path as NSString) {
return .success(tile_data as String)
}
```See the way we're calling `tiles_cache.missing.object` and `tiles_cache.cache.object` ? These are not ideal interfaces for dealing with tile caching. What's really needed is an interface for _tiles_ that sits on top of a generic interface for caching and there hasn't been the luxury of time to figure that out yet. It is definitely an area for improvement.
Assuming there isn't a cached version (and we don't know that the tile is missing) the tile data would be retrieved like this:
```
let tile_path = "tiles/example/10/12/345.png"var tile: MBTile
let tile_rsp = tiles_resolver.MBTileFromPath(path: tile_path)
switch tile_rsp {
case .failure(let error):
// handle error here
case .success(let t):
tile = t
}
let db_path = tiles_collection.DatabasePathFromTile(tile: tile)
let data_rsp = tiles_reader.ReadTileAsDataURL(db_pool: tiles_pool, db_path: db_path, tile: tile)
switch data_rsp {
case .failure(let error):
// handle error here
case .success(let tile_data):
tiles_cache.cache.setObject(NSString(string: tile_data), forKey:NSString(string: tile_path))
// do something with tile data here
}
```## MBTilesResolver
```
public protocol MBTilesResolver {
func PrefixFromPath(path: String) -> Result
func MBTileFromPath(path: String) -> Result
func PathFromMBTile(tile: MBTile) -> Result
}
```## See also
* https://github.com/stephencelis/SQLite.swift