Consolidate / dedupe some code

This commit is contained in:
stopflock
2025-08-24 17:46:58 -05:00
parent bedfdcca6e
commit 9e620ef9e4
6 changed files with 95 additions and 190 deletions
+38 -89
View File
@@ -13,77 +13,50 @@ class SimpleTileHttpClient extends http.BaseClient {
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
// Try to parse as a tile request from any provider
final tileInfo = _parseTileRequest(request.url);
if (tileInfo != null) {
return _handleTileRequest(request, tileInfo);
// Extract tile coordinates from the URL using our standard pattern
final tileCoords = _extractTileCoords(request.url);
if (tileCoords != null) {
final z = tileCoords['z']!; // We know these are not null from _extractTileCoords
final x = tileCoords['x']!;
final y = tileCoords['y']!;
return _handleTileRequest(z, x, y);
}
// Pass through non-tile requests
return _inner.send(request);
}
/// Parse URL to extract tile coordinates if it looks like a tile request
Map<String, dynamic>? _parseTileRequest(Uri url) {
/// Extract z/x/y coordinates from our standard tile URL pattern
Map<String, int>? _extractTileCoords(Uri url) {
// We'll use a simple standard pattern: /{z}/{x}/{y}.png
// This will be the format we use in map_view.dart
final pathSegments = url.pathSegments;
// Common patterns for tile URLs:
// OSM: /z/x/y.png
// Google: /vt/lyrs=y&x=x&y=y&z=z (query params)
// Mapbox: /styles/v1/mapbox/streets-v12/tiles/z/x/y
// ArcGIS: /tile/z/y/x.png
// Try query parameters first (Google style)
final query = url.queryParameters;
if (query.containsKey('x') && query.containsKey('y') && query.containsKey('z')) {
final x = int.tryParse(query['x']!);
final y = int.tryParse(query['y']!);
final z = int.tryParse(query['z']!);
if (x != null && y != null && z != null) {
return {'z': z, 'x': x, 'y': y, 'originalUrl': url.toString()};
}
}
// Try path-based patterns
if (pathSegments.length >= 3) {
// Try z/x/y pattern (OSM style) - can be at different positions
for (int i = 0; i <= pathSegments.length - 3; i++) {
final z = int.tryParse(pathSegments[i]);
final x = int.tryParse(pathSegments[i + 1]);
final yWithExt = pathSegments[i + 2];
final y = int.tryParse(yWithExt.replaceAll(RegExp(r'\.[^.]*$'), '')); // Remove file extension
if (z != null && x != null && y != null) {
return {'z': z, 'x': x, 'y': y, 'originalUrl': url.toString()};
}
}
}
return null; // Not a recognizable tile request
}
Future<http.StreamedResponse> _handleTileRequest(http.BaseRequest request, Map<String, dynamic> tileInfo) async {
final z = tileInfo['z'] as int;
final x = tileInfo['x'] as int;
final y = tileInfo['y'] as int;
final originalUrl = tileInfo['originalUrl'] as String;
return _getTile(z, x, y, originalUrl, request.url.host);
}
Future<http.StreamedResponse> _getTile(int z, int x, int y, String originalUrl, String providerHost) async {
try {
// First try to get tile from offline storage
final localTileBytes = await _mapDataProvider.getTile(z: z, x: x, y: y, source: MapSource.local);
if (pathSegments.length == 3) {
final z = int.tryParse(pathSegments[0]);
final x = int.tryParse(pathSegments[1]);
final yWithExt = pathSegments[2];
final y = int.tryParse(yWithExt.replaceAll(RegExp(r'\.[^.]*$'), '')); // Remove .png
debugPrint('[SimpleTileService] Serving tile $z/$x/$y from offline storage');
if (z != null && x != null && y != null) {
return {'z': z, 'x': x, 'y': y};
}
}
return null;
}
Future<http.StreamedResponse> _handleTileRequest(int z, int x, int y) async {
try {
// Always go through MapDataProvider - it handles offline/online routing
final tileBytes = await _mapDataProvider.getTile(z: z, x: x, y: y, source: MapSource.auto);
// Clear waiting status - we got data
NetworkStatus.instance.clearWaiting();
// Serve offline tile with proper cache headers
// Serve tile with proper cache headers
return http.StreamedResponse(
Stream.value(localTileBytes),
Stream.value(tileBytes),
200,
headers: {
'Content-Type': 'image/png',
@@ -94,39 +67,15 @@ class SimpleTileHttpClient extends http.BaseClient {
);
} catch (e) {
// No offline tile available
debugPrint('[SimpleTileService] No offline tile for $z/$x/$y');
debugPrint('[SimpleTileService] Could not get tile $z/$x/$y: $e');
// Check if we're in offline mode
if (AppState.instance.offlineMode) {
debugPrint('[SimpleTileService] Offline mode - not attempting $providerHost fetch for $z/$x/$y');
// Report that we couldn't serve this tile offline
NetworkStatus.instance.reportOfflineMiss();
return http.StreamedResponse(
Stream.value(<int>[]),
404,
reasonPhrase: 'Tile not available offline',
);
}
// We're online - try the original provider with proper error handling
debugPrint('[SimpleTileService] Online mode - trying $providerHost for $z/$x/$y');
try {
final response = await _inner.send(http.Request('GET', Uri.parse(originalUrl)));
// Clear waiting status on successful network tile
if (response.statusCode == 200) {
NetworkStatus.instance.clearWaiting();
}
return response;
} catch (networkError) {
debugPrint('[SimpleTileService] $providerHost request failed for $z/$x/$y: $networkError');
// Return 404 instead of throwing - let flutter_map handle gracefully
return http.StreamedResponse(
Stream.value(<int>[]),
404,
reasonPhrase: 'Network tile unavailable: $networkError',
);
}
// Let MapDataProvider handle offline mode logic
// Just return 404 and let flutter_map handle it gracefully
return http.StreamedResponse(
Stream.value(<int>[]),
404,
reasonPhrase: 'Tile unavailable: $e',
);
}
}