fix: support raster MBTiles import and derive center from bounds#474
fix: support raster MBTiles import and derive center from bounds#474torlando-tech merged 5 commits intomainfrom
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis PR fixes two issues with MBTiles imports: (1) maps without
Confidence Score: 4/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant VM as OfflineMapsViewModel
participant Repo as OfflineMapRegionRepository
participant Builder as OfflineMapStyleBuilder
participant DB as SQLiteDatabase
participant FS as FileSystem
User->>VM: importMbtilesFile(uri)
VM->>FS: Copy URI to offline_maps/
VM->>Repo: importOrphanedFile(file)
Repo->>DB: Open MBTiles SQLite (extractMbtilesMetadata)
DB-->>Repo: metadata (name, bounds, center, zoom)
Note over Repo: Derive center from bounds<br/>if center metadata missing
Repo->>Repo: Insert OfflineMapRegionEntity
Repo-->>VM: regionId
VM->>VM: cacheStyleForRegion(regionId, file, name)
VM->>Builder: buildAutoOfflineStyle(path, name)
Builder->>DB: Open MBTiles SQLite (getTileFormat)
DB-->>Builder: format (pbf/png/jpg)
alt format == "pbf"
Builder->>Builder: buildOfflineStyle (vector)
else format != "pbf"
Builder->>Builder: buildRasterOfflineStyle (raster)
end
Builder-->>VM: styleJson
VM->>FS: Write offline_styles/{regionId}.json
VM->>Repo: updateLocalStylePath(regionId, path)
Last reviewed commit: 5a9b6c3 |
| fun getTileFormat(mbtilesPath: String): String { | ||
| var db: android.database.sqlite.SQLiteDatabase? = null | ||
| return try { | ||
| db = | ||
| android.database.sqlite.SQLiteDatabase.openDatabase( | ||
| mbtilesPath, | ||
| null, | ||
| android.database.sqlite.SQLiteDatabase.OPEN_READONLY, | ||
| ) | ||
| db | ||
| .rawQuery( | ||
| "SELECT value FROM metadata WHERE name = 'format'", | ||
| null, | ||
| ).use { cursor -> | ||
| if (cursor.moveToFirst()) cursor.getString(0) else "png" | ||
| } | ||
| } catch ( | ||
| @Suppress("SwallowedException") e: Exception, | ||
| ) { | ||
| Log.w("OfflineMapStyleBuilder", "Failed to read tile format from $mbtilesPath", e) | ||
| "png" | ||
| } finally { | ||
| db?.close() | ||
| } | ||
| } |
There was a problem hiding this comment.
Duplicate SQLite access pattern
getTileFormat opens the MBTiles SQLite database and reads from the metadata table, which is the same pattern already used by OfflineMapRegionRepository.extractMbtilesMetadata. Both callers are invoked during the same import flow — extractMbtilesMetadata when creating the region entity, and getTileFormat when generating the style.
Consider extracting the format field during extractMbtilesMetadata (which already reads SELECT name, value FROM metadata) and passing it through, rather than opening the database a second time. This would avoid the extra file I/O and be more maintainable.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/com/lxmf/messenger/map/OfflineMapStyleBuilder.kt
Line: 275:299
Comment:
**Duplicate SQLite access pattern**
`getTileFormat` opens the MBTiles SQLite database and reads from the `metadata` table, which is the same pattern already used by `OfflineMapRegionRepository.extractMbtilesMetadata`. Both callers are invoked during the same import flow — `extractMbtilesMetadata` when creating the region entity, and `getTileFormat` when generating the style.
Consider extracting the `format` field during `extractMbtilesMetadata` (which already reads `SELECT name, value FROM metadata`) and passing it through, rather than opening the database a second time. This would avoid the extra file I/O and be more maintainable.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.| fun buildAutoOfflineStyle( | ||
| mbtilesPath: String, | ||
| name: String = "Offline Map", | ||
| ): String { | ||
| val format = getTileFormat(mbtilesPath) | ||
| Log.d("OfflineMapStyleBuilder", "Detected tile format '$format' for $mbtilesPath") | ||
| return if (format == "pbf") { | ||
| buildOfflineStyle(mbtilesPath, name) | ||
| } else { | ||
| buildRasterOfflineStyle(mbtilesPath, name) | ||
| } | ||
| } |
There was a problem hiding this comment.
Missing unit tests for new public methods
buildAutoOfflineStyle, buildRasterOfflineStyle, and getTileFormat are all new public methods but there are no corresponding tests in OfflineMapStyleBuilderTest. The PR description notes existing tests pass, but the test plan shows vector MBTiles verification is unchecked. At minimum, buildRasterOfflineStyle can be tested (like the existing buildOfflineStyle tests) to verify the generated JSON structure contains a raster source and raster layer.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/src/main/java/com/lxmf/messenger/map/OfflineMapStyleBuilder.kt
Line: 256:267
Comment:
**Missing unit tests for new public methods**
`buildAutoOfflineStyle`, `buildRasterOfflineStyle`, and `getTileFormat` are all new public methods but there are no corresponding tests in `OfflineMapStyleBuilderTest`. The PR description notes existing tests pass, but the test plan shows vector MBTiles verification is unchecked. At minimum, `buildRasterOfflineStyle` can be tested (like the existing `buildOfflineStyle` tests) to verify the generated JSON structure contains a raster source and raster layer.
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.MBTiles files without a "center" metadata field (common in exports from MeshChatX and other tools) defaulted to 0,0 — showing the Gulf of Guinea instead of the actual region. Now falls back to calculating center from bounds metadata. Also adds raster tile rendering support: imported PNG/JPEG MBTiles files now generate a raster style (instead of the hardcoded vector style) so they actually render on the map. The format is auto-detected from the MBTiles metadata table during import. Fixes #296 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Build a combined MapLibre style from all completed MBTiles regions, auto-detecting raster vs vector format for each. Raster layers render below vector layers so both types coexist. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refresh the default region on resume so changing the favorite immediately animates the camera to the new region center. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous approach (minZoom or radius-based) was too zoomed out for tiles to render. Use the region's maxZoom (clamped to 14) which is the most detailed level the tiles support and guarantees visible rendering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5a9b6c3 to
4ba5b09
Compare
app/src/main/java/com/lxmf/messenger/map/MapTileSourceManager.kt
Outdated
Show resolved
Hide resolved
…read - Update MapTileSourceManagerTest to pass Context as first constructor arg - Update OfflineMapStyleBuilderTest for new List<String> signature - Stub getCompletedRegionsWithMbtiles in test setup - Move combined style build + file write to Dispatchers.IO - Use maxZoom for favorite region initial zoom level Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| }, | ||
| ) | ||
| if (!hasVector) { | ||
| vectorSourceId = sourceId |
There was a problem hiding this comment.
Bug: When combining multiple vector MBTiles files, only the first file's map data is rendered because all layers are incorrectly pointed to the first vector source.
Severity: HIGH
Suggested Fix
Modify the layer generation logic. Instead of replacing the source on a single BASE_LAYERS template with the first vectorSourceId, iterate through each detected vector source. For each vector source, create a distinct set of layers that correctly reference that source's unique ID. This ensures that layers are generated for every vector file, allowing all of them to be rendered on the map.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: app/src/main/java/com/lxmf/messenger/map/OfflineMapStyleBuilder.kt#L178-L181
Potential issue: The `buildCombinedOfflineStyle` function is designed to combine
multiple MBTiles files. When multiple vector (pbf) files are provided, it correctly
creates a source for each (e.g., `vector-0`, `vector-1`). However, it only stores the ID
of the first source in the `vectorSourceId` variable. Subsequently, all base vector
layers are configured to use only this first source ID. This results in a map where only
the tiles from the first imported vector file are rendered, while all other vector files
are invisible, despite being processed.
Summary
centermetadata defaulting to 0,0 (Gulf of Guinea) — now derives center fromboundsmetadataTest plan
OfflineMapStyleBuilderTest,OfflineMapsViewModelTest)Fixes #296
🤖 Generated with Claude Code