[Bug]: Offline pack not visible in mapview when device is offline
Mapbox Implementation
Mapbox
Mapbox Version
default
React Native Version
0.76.0
Platform
Android
@rnmapbox/maps version
10.1.36
Standalone component to reproduce
import React, { useCallback, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import {
View,
Text,
TextInput,
StyleSheet,
FlatList,
Modal,
Pressable,
Alert,
TouchableOpacity,
ToastAndroid,
KeyboardAvoidingView,
Dimensions,
Image,
} from 'react-native';
import Mapbox from '@rnmapbox/maps';
import { useFocusEffect } from '@react-navigation/native';
import IonIcons from 'react-native-vector-icons/Ionicons';
import Geolocation from '@react-native-community/geolocation';
import { Picker } from '@react-native-picker/picker';
const MAPBOX_API_KEY = 'API_KEY';
const MAPBOX_SATELITE_STYLE = 'mapbox://styles/mapbox/satellite-streets-v12';
const MAPBOX_OUTDOOR_STYLE = 'mapbox://styles/mapbox/outdoors-v12';
Mapbox.setAccessToken(MAPBOX_API_KEY);
const MapRegionCaching = () => {
const [offlineRegions, setOfflineRegions] = useState([]);
const [isModalVisible, setModalVisible] = useState(false);
const [regionName, setRegionName] = useState('');
const [isDownloading, setIsDownloading] = useState(false);
const [downloadProgress, setDownloadProgress] = useState(0);
const [centerCoordinates, setCenterCoordinates] = useState();
const [selectedRegion, setSelectedRegion] = useState();
const [selectedMap, setSelectedMap] = useState();
const [selectedMapStyle, setSelectedMapStyle] = useState(MAPBOX_OUTDOOR_STYLE);
const [showMap, setShowMap] = useState(false);
const mapCameraRef = useRef();
const [mapStyle, setMapStyle] = useState(MAPBOX_OUTDOOR_STYLE);
const [zoomLevel, setZoomLevel] = useState(12);
const [zoomOpen, setZoomOpen] = useState(false);
const zoomLevels = Array.from({ length: 23 }, (_, i) => ({
label: `Zoom Level ${i}${i === 12 ? ' (Standard)' : ''}`,
value: i,
}));
// Save a new offline region
const saveOfflineRegion = async () => {
if (!regionName.trim() || !selectedRegion) {
Alert.alert('Error', 'A region name is required and region is required to be saved.');
return;
}
const isExists = offlineRegions.find(
(item) => item?.metadata?.name === regionName
);
if (isExists) {
ToastAndroid.show('Duplicate Region Name', ToastAndroid.SHORT);
return;
}
console.log('Creating offline ...');
try {
setModalVisible(false);
setIsDownloading(true);
ToastAndroid.show('Starting Download...', ToastAndroid.SHORT);
Mapbox.offlineManager.setTileCountLimit(9999999);
const offlineRegion = await Mapbox.offlineManager.createPack(
{
bounds: selectedRegion,
name: regionName,
minZoom: 18,
maxZoom: 20,
styleURL: mapStyle,
},
(progress) => {
console.log('PROGRESS ====> ', progress);
progress.status().then((value) => {
console.log('STATUS ====> ', value);
setDownloadProgress(value.percentage.toFixed(2));
if (value.percentage === 100) {
setIsDownloading(false);
setDownloadProgress(0);
ToastAndroid.show('Downloaded Successfully', ToastAndroid.SHORT);
setRegionName('');
setZoomLevel(12);
setSelectedRegion();
loadOfflineMaps();
}
});
},
(error) => {
Mapbox.offlineManager.deletePack(regionName);
console.log('DOWNLOAD_ERROR ====> ', error);
setIsDownloading(false);
setDownloadProgress(0);
setModalVisible(true);
ToastAndroid.show("Couldn't Download! please try smaller regions", ToastAndroid.LONG);
error.status().then((value) => {
console.log('ERROR_STATUS ====> ', value);
});
}
);
console.log('OFFLINE REGION ====> ', offlineRegion);
} catch (error) {
Mapbox.offlineManager.deletePack(regionName);
ToastAndroid.show(error.toString(), ToastAndroid.SHORT);
console.log('ERROR ====> ', error);
}
};
const loadOfflineMaps = async () => {
try {
const offlineMaps = await Mapbox.offlineManager.getPacks();
setOfflineRegions(offlineMaps);
console.log('OFFLINE_MAPS ===> ', JSON.stringify(offlineMaps));
} catch (error) {
console.log('ERROR ====> ', error.toString());
}
};
const cancelDownload = async (packName) => {
try {
const deletedPack = await Mapbox.offlineManager.deletePack(packName);
console.log('CANCELLED_DOWNLOAD ====> ', deletedPack);
ToastAndroid.show('Download Cancelled', ToastAndroid.SHORT);
setIsDownloading(false);
setDownloadProgress(0);
setModalVisible(true);
} catch (error) {
ToastAndroid.show("Couldn't Cancel Download", ToastAndroid.SHORT);
console.log('ERROR ===> ', error);
}
};
const deleteMap = async (packName) => {
try {
const deletedPack = await Mapbox.offlineManager.deletePack(packName);
console.log('MAP_DELETED ====> ', deletedPack);
ToastAndroid.show(packName + ' Deleted', ToastAndroid.SHORT);
loadOfflineMaps();
} catch (error) {
ToastAndroid.show("Couldn't Delete " + packName, ToastAndroid.SHORT);
console.log('ERROR ===> ', error);
}
};
useFocusEffect(
useCallback(() => {
Geolocation.getCurrentPosition((info) => {
console.log(info);
setCenterCoordinates([info.coords.longitude, info.coords.latitude]);
});
loadOfflineMaps();
}, [])
);
const rectangleGeoJSON = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: selectedMap?._metadata?._rnmapbox?.bounds,
properties: {},
},
],
};
// Render saved offline regions
const renderOfflineRegion = ({ item }) => (
<View style={styles.regionItem}>
<View style={styles.regionItemContainer}>
<View style={styles.infoContainer}>
<IonIcons name="map" size={40} color="#4CAF50" style={styles.icon} />
<View style={styles.textContainer}>
<Text style={styles.regionName}>
{item?.metadata?.name || 'Unknown Region'}
</Text>
<Text style={styles.regionInfo}>
Expires on: {item?.pack?.expires}
</Text>
</View>
</View>
<View style={styles.actionIcons}>
<TouchableOpacity
onPress={() => {
console.log('SELECTED_MAP ====> ', JSON.stringify(item));
setSelectedMap(item);
setShowMap(true);
setTimeout(() => {
mapCameraRef?.current?.fitBounds(
[item?.pack?.bounds[0], item?.pack?.bounds[1]],
[item?.pack?.bounds[2], item?.pack?.bounds[3]],
[50, 50],
2000
);
}, 1000);
}}
style={styles.iconButton}
>
<IonIcons name="eye" size={24} color="#4CAF50" />
</TouchableOpacity>
<TouchableOpacity
onPress={() => deleteMap(item?.metadata?.name)}
style={styles.iconButton}
>
<IonIcons name="trash" size={24} color="#F44336" />
</TouchableOpacity>
</View>
</View>
</View>
);
return (
<View style={styles.container}>
{/* List of Offline Regions */}
<FlatList
data={offlineRegions}
renderItem={renderOfflineRegion}
keyExtractor={(item, index) => index.toString()}
contentContainerStyle={styles.regionList}
ListEmptyComponent={
<Text style={styles.emptyText}>No offline regions saved.</Text>
}
/>
{/* Floating Action Button */}
<TouchableOpacity
style={styles.fab}
onPress={() => setModalVisible(true)}
>
<Text style={styles.fabText}>+</Text>
</TouchableOpacity>
<ProgressModal
title="Downloading"
progress={downloadProgress}
cancelButtonTitle="Cancel Download"
visible={isDownloading}
onCancelPress={() => cancelDownload(regionName)}
/>
<Modal
visible={showMap}
animationType="slide"
transparent={true}
onRequestClose={() => setShowMap(false)}
>
<KeyboardAvoidingView style={{ flex: 1 }}>
<View style={styles.modalBackground}>
<View style={styles.modalContent}>
<Text style={styles.modalHeader}>View Offline Map</Text>
<View style={styles.mapContainer}>
<Mapbox.MapView
logoEnabled={false}
attributionEnabled={false}
style={{ ...styles.map, width: '100%', borderWidth: 0 }}
styleURL={selectedMap?._metadata?._rnmapbox?.styleURI}
>
<Mapbox.Camera
animationDuration={2000}
zoomLevel={12}
ref={mapCameraRef}
bounds={{
ne: [
selectedMap?.pack?.bounds[0],
selectedMap?.pack?.bounds[1],
],
sw: [
selectedMap?.pack?.bounds[2],
selectedMap?.pack?.bounds[3],
],
}}
/>
<Mapbox.ShapeSource id="rectangleSource" shape={rectangleGeoJSON}>
<Mapbox.FillLayer
id="rectangleFill"
style={{
fillColor: 'rgba(0, 150, 200, 0.5)',
fillOutlineColor: 'rgba(0, 150, 200, 1)',
}}
/>
</Mapbox.ShapeSource>
</Mapbox.MapView>
</View>
<View style={styles.modalActions}>
<Pressable
style={[styles.modalButton, styles.cancelButton]}
onPress={() => {
setShowMap(false);
setSelectedMap();
}}
>
<Text style={styles.buttonText}>Close</Text>
</Pressable>
</View>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
{/* Modal for Adding Offline Region */}
<Modal
visible={isModalVisible}
animationType="slide"
transparent={true}
onRequestClose={() => setModalVisible(false)}
>
<KeyboardAvoidingView style={{ flex: 1 }}>
<View style={styles.modalBackground}>
<View style={styles.modalContent}>
<Text style={styles.modalHeader}>Add Offline Region</Text>
<View
style={{
width: '100%',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
}}
>
<TextInput
style={[
styles.input,
{
width: '75%',
height: '100%',
},
]}
placeholder="Region Name"
value={regionName}
onChangeText={setRegionName}
/>
<View style={[styles.input, { width: '24%', height: 'auto' }]}>
<Picker
selectedValue={zoomLevel}
onValueChange={(itemValue) => setZoomLevel(itemValue)}
style={{ padding: 0 }}
placeholder="Zoom Level"
>
{zoomLevels.map((z) => (
<Picker.Item
key={`zoom-level-${z.value}`}
label={z.label}
value={z.value}
/>
))}
</Picker>
</View>
</View>
<Text style={styles.label}>
Select Map Region (Zoom and Pan to set region visible. Map area will be saved for offline use.)
</Text>
<View style={styles.mapContainer}>
<TouchableOpacity
style={styles.mapStyle2}
onPress={() => {
if (mapStyle === MAPBOX_OUTDOOR_STYLE) {
setMapStyle(MAPBOX_SATELITE_STYLE);
} else {
setMapStyle(MAPBOX_OUTDOOR_STYLE);
}
}}
>
{mapStyle === MAPBOX_OUTDOOR_STYLE ? (
<Image
source={require('../../assets/satellite_map_icon.png')}
style={styles.mapStyleIcon}
/>
) : (
<Image
source={require('../../assets/street_map_icon.png')}
style={styles.mapStyleIcon}
/>
)}
</TouchableOpacity>
<Mapbox.MapView
logoEnabled={false}
attributionEnabled={false}
style={styles.map}
styleURL={mapStyle}
onRegionDidChange={(state) => {
setSelectedRegion(state.properties.visibleBounds);
console.log(
'MAP REGION CHANGED ====> ',
JSON.stringify(state.properties)
);
}}
>
<Mapbox.Camera
zoomLevel={12}
centerCoordinate={centerCoordinates}
/>
</Mapbox.MapView>
</View>
<View style={styles.modalActions}>
<Pressable
style={[styles.modalButton, styles.cancelButton]}
onPress={() => {
setModalVisible(false);
setRegionName('');
setZoomLevel(12);
setSelectedRegion();
}}
>
<Text style={styles.buttonText}>Cancel</Text>
</Pressable>
<Pressable style={styles.modalButton} onPress={saveOfflineRegion}>
<Text style={styles.buttonText}>Save</Text>
</Pressable>
</View>
</View>
</View>
</KeyboardAvoidingView>
</Modal>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F5F5',
},
regionList: {
paddingHorizontal: 16,
paddingBottom: 80,
},
regionItemContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
regionItem: {
backgroundColor: '#fff',
padding: 10,
marginVertical: 8,
borderRadius: 8,
elevation: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
infoContainer: {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
},
icon: {
marginRight: 10,
},
textContainer: {
flex: 1,
},
regionName: {
fontSize: 16,
fontWeight: 'bold',
color: '#212121',
},
regionInfo: {
fontSize: 14,
color: '#757575',
marginTop: 4,
},
actionIcons: {
flexDirection: 'row',
alignItems: 'center',
},
iconButton: {
marginHorizontal: 8,
},
label: {
fontSize: 18,
color: '#757575',
marginTop: 4,
marginBottom: 8,
textAlign: 'center',
},
emptyText: {
textAlign: 'center',
marginTop: 20,
fontSize: 16,
color: '#757575',
},
fab: {
position: 'absolute',
right: 20,
bottom: 30,
backgroundColor: '#4CAF50',
width: 56,
height: 56,
borderRadius: 8,
justifyContent: 'center',
alignItems: 'center',
elevation: 5,
},
fabText: {
fontSize: 30,
color: '#FFF',
fontWeight: 'bold',
},
modalBackground: {
height: Dimensions.get('window').height,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContent: {
width: '90%',
backgroundColor: '#FFF',
padding: 16,
borderRadius: 8,
elevation: 4,
},
modalHeader: {
fontSize: 20,
fontWeight: 'bold',
color: '#212121',
marginBottom: 16,
textAlign: 'left',
},
input: {
height: 48,
borderWidth: 1,
borderColor: '#BDBDBD',
borderRadius: 4,
paddingHorizontal: 12,
backgroundColor: '#FAFAFA',
},
mapContainer: {
height: 400,
width: '100%',
borderRadius: 8,
marginBottom: 16,
overflow: 'hidden',
justifyContent: 'center',
alignItems: 'center',
position: 'relative',
},
map: {
flex: 1,
height: 400,
width: 400,
borderWidth: 5,
borderColor: '#4CAF50',
position: 'relative',
},
mapStyle2: {
position: 'absolute',
bottom: 20,
right: 220,
width: 70,
height: 70,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderRadius: 10,
overflow: 'hidden',
zIndex: 1,
},
mapStyleIcon: {
width: 70,
height: 70,
},
modalActions: {
flexDirection: 'row',
justifyContent: 'space-between',
},
modalButton: {
flex: 1,
backgroundColor: '#4CAF50',
paddingVertical: 12,
borderRadius: 4,
alignItems: 'center',
marginHorizontal: 4,
},
cancelButton: {
backgroundColor: '#757575',
},
buttonText: {
color: '#FFF',
fontSize: 16,
fontWeight: 'bold',
},
});
export default MapRegionCaching;
const ProgressModal = ({
visible,
progress,
onClose,
cancelButtonTitle,
onCancelPress,
title,
}) => {
return (
<Modal
transparent
animationType="slide"
visible={visible}
onRequestClose={onClose}
>
<View style={styles1.modalBackground}>
<View style={styles1.modalContainer}>
<Text style={styles1.modalTitle}>{title}</Text>
<View style={styles1.progressContainer}>
<View style={styles1.progressBar}>
<View
style={[styles1.progressFill, { width: `${progress}%` }]}
/>
</View>
<Text style={styles1.progressText}>{`${progress}%`}</Text>
</View>
<View style={styles1.buttonContainer}>
<TouchableOpacity
style={[styles1.button, styles1.cancelButton]}
onPress={onCancelPress}
>
<Text style={styles1.buttonText}>{cancelButtonTitle}</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
};
ProgressModal.propTypes = {
visible: PropTypes.bool.isRequired,
progress: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
onClose: PropTypes.func,
cancelButtonTitle: PropTypes.string.isRequired,
onCancelPress: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
};
const styles1 = StyleSheet.create({
modalBackground: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
modalContainer: {
width: '80%',
backgroundColor: '#FFFFFF',
borderRadius: 8,
padding: 16,
alignItems: 'flex-start',
elevation: 5,
},
modalTitle: {
fontSize: 18,
fontWeight: 'bold',
marginBottom: 16,
},
progressContainer: {
width: '100%',
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
progressBar: {
width: '95%',
height: 10,
backgroundColor: '#E0E0E0',
borderRadius: 5,
overflow: 'hidden',
},
progressFill: {
height: '100%',
backgroundColor: '#4CAF50',
},
progressText: {
textAlign: 'center',
fontSize: 16,
fontWeight: 'bold',
color: '#333',
width: '5%',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'flex-end',
width: '100%',
},
button: {
flex: 1,
padding: 12,
backgroundColor: '#757575',
borderRadius: 8,
marginHorizontal: 8,
alignItems: 'center',
},
cancelButton: {},
buttonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: 'bold',
},
});
Observed behavior and steps to reproduce
we area download a area of map using createPack method and we getting that in packs by calling getPack() method but we are not able to see that area of map when there is internet connection besically we area downloading area of map at certain zoom level so that we can see that area when there is not internet connection we followed everything from dcoumentation and every issue reported nothing is working
LOGS i have recorded
Logs of Downloading offline pack.----- from zoom level 18,20
PROGRESS ====> {"_metadata": null, "pack": {"bounds": "{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-79.378419567841,43.665287295057084]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-79.40243501061582,43.64791235618151]}}]}", "metadata": "{"name":"Toronto sat "}"}} (NOBRIDGE) LOG STATUS ====> {"completedResourceCount": 0, "completedResourceSize": 0, "erroredResourceCount": 0, "loadedResourceCount": 0, "loadedResourceSize": 0, "metadata": {"_rnmapbox": {"bounds": [Object], "styleURI": "mapbox://styles/mapbox/satellite-streets-v12", "zoomRange": [Array]}, "name": "Toronto sat "}, "name": "Toronto sat ", "percentage": 0, "requiredResourceCount": 1, "state": "unkown"} (NOBRIDGE) LOG PROGRESS ====> {"_metadata": {"name": "Toronto sat "}, "pack": {"bounds": "{"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-79.378419567841,43.665287295057084]}},{"type":"Feature","properties":{},"geometry":{"type":"Point","coordinates":[-79.40243501061582,43.64791235618151]}}]}", "metadata": "{"name":"Toronto sat "}"}} (NOBRIDGE) LOG STATUS ====> {"completedResourceCount": 1, "completedResourceSize": 8648487, "erroredResourceCount": 0, "loadedResourceCount": 0, "loadedResourceSize": 0, "metadata": {"_rnmapbox": {"bounds": [Object], "styleURI": "mapbox://styles/mapbox/satellite-streets-v12", "zoomRange": [Array]}, "name": "Toronto sat "}, "name": "Toronto sat ", "percentage": 100, "requiredResourceCount": 1, "state": "complete"} (NOBRIDGE) LOG OFFLINE_MAPS ===> [{"pack":{"expires":"Sun Mar 23 17:51:06 GMT+05:30 2025","percentage":100,"bounds":[-79.378419567841,43.66528729505709,-79.40243501061582,43.64791235618151],"completedResourceSize":8648487,"metadata":"{"name":"Toronto sat ","_rnmapbox":{"bounds":{"coordinates":[[[-79.3784196,43.6652873],[-79.402435,43.6652873],[-79.402435,43.6479124],[-79.3784196,43.6479124],[-79.3784196,43.6652873]]],"type":"Polygon"},"zoomRange":[18,20],"styleURI":"mapbox:\/\/styles\/mapbox\/satellite-streets-v12"}}","state":"complete","completedResourceCount":1,"requiredResourceCount":1},"_metadata":{"name":"Toronto sat ","_rnmapbox":{"bounds":{"coordinates":[[[-79.3784196,43.6652873],[-79.402435,43.6652873],[-79.402435,43.6479124],[-79.3784196,43.6479124],[-79.3784196,43.6652873]]],"type":"Polygon"},"zoomRange":[18,20],"styleURI":"mapbox://styles/mapbox/satellite-streets-v12"}}}]
when viewing map with no network its just cache is visible which was there when downloaded it at zoom 13 just those cache tiles are visible when i am zooming it to level 18-20 no detailed tiles are visible of that region i created pack for.
these log data which offline device trying to view offline pack in mapview
(NOBRIDGE) ERROR Mapbox [error] RNMBXMapView | Map load failed: {tile-id={x=293049, y=382662, z=20}, type=tile, message=Failed to load tile: Unable to resolve host "api.mapbox.com": No address associated with hostname, begin=86303505788, source-id=mapbox-satellite} (NOBRIDGE) LOG CAMERA_STATE ====> {"gestures": {"isGestureActive": true}, "properties": {"bounds": {"ne": [Array], "sw": [Array]}, "center": [-79.39018702067013, 43.65789523675591], "heading": 354.00065729767084, "pitch": 0, "zoom": 19.07918573547368}, "timestamp": 1740140840016}
SCREENSHOTS when device is offline for the downloaded area at same zoom levels which we have downloaded for.
Expected behavior
Downloaded area of map should be visible when there is no internet connection to device
Notes / preliminary analysis
No response
Additional links and references
No response
am also facing the same issue , i was downloading the app , but it was not showing as the zoom level of 20 , i thick it was only 13 zoom level only
@sampathkumarch you mean offline packs are working till zoom level 13 only?
yes , I also worked on the same thing but was unable to get the clarity of tiles
On Fri, Feb 28, 2025 at 1:04 PM NITESH SAHNI @.***> wrote:
@sampathkumarch https://github.com/sampathkumarch you mean offline packs are working till zoom level 13 only?
— Reply to this email directly, view it on GitHub https://github.com/rnmapbox/maps/issues/3790#issuecomment-2689939117, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL6WXQYM5XVUXISO6DN4IJD2SAGSDAVCNFSM6AAAAABXYNHLP2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMOBZHEZTSMJRG4 . You are receiving this because you were mentioned.Message ID: @.***> [image: sahni01]sahni01 left a comment (rnmapbox/maps#3790) https://github.com/rnmapbox/maps/issues/3790#issuecomment-2689939117
@sampathkumarch https://github.com/sampathkumarch you mean offline packs are working till zoom level 13 only?
— Reply to this email directly, view it on GitHub https://github.com/rnmapbox/maps/issues/3790#issuecomment-2689939117, or unsubscribe https://github.com/notifications/unsubscribe-auth/AL6WXQYM5XVUXISO6DN4IJD2SAGSDAVCNFSM6AAAAABXYNHLP2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDMOBZHEZTSMJRG4 . You are receiving this because you were mentioned.Message ID: @.***>
anybody got any solution for it or any working example? if its not working then why its even there in documentation ?
@sampathkumarch I've resolved the issue—a small bug in the Kotlin implementation was causing the problem. To isolate the issue, I first implemented a Kotlin-only version to determine whether it was related to Mapbox or our repository. It turned out that the problem was in the repository itself. I've created a PR with the fix, and now the tiles download correctly, render on the map view (even when the device is offline), and all Mapbox styles work as expected.
@sahni01, Can I know where I have to be changes are required like kotlin file , can you guide me in detail manner . It help to me also thank you for support
@sahni01, What are the changes are required to get max zoom level can you explain it , i didn't see the any PR
@sahni01, Hello, I have the same problem as above. I'd like to know what changes you've made to correct the implementation?
Hello , i have the same problem as above
If you're interested in downloading high-resolution tiles (zoom 22 max), you'll need to use offlineManagerLegacy class. From what I understand, the new offlineManager class only allows downloading in the zoom range 1 to 16 (source).
Here's a sample code for downloading high-resolution tiles (with @rnmapbox : 10.1.37) :
import { Bounds } from "@models/bounds";
import Mapbox from "@rnmapbox/maps";
async function checkPackDownloading(zone: string, onProgress: (progress: number) => void, onFatalError: (error: Error) => void){
const pack = await Mapbox.offlineManagerLegacy.getPack(zone);
if (pack != undefined) {
const status = await pack.status();
console.log(status);
// Mapbox.OfflinePackDownloadState is not fully compatible with offlineManagerLegacy, avoid using it directly
if (status.state === 2) { // Completed
onProgress(100);
} else if (status.state === 0 && status.percentage != 100) { // Fatal Error
console.error("Zone download paused", zone);
onFatalError(new Error("TILE_DOWNLOAD_PAUSED"));
} else if (status.state === 1) { // In progress
onProgress(status.percentage);
setTimeout(() => {
checkPackDownloading(zone, onProgress, onFatalError);
}, 2000);
} else {
onFatalError(new Error(`UNEXPECTED_STATE (${status.state})`));
}
} else {
onFatalError(new Error("PACK_NOT_FOUND"));
}
}
export async function fetchPack(zone: string, bounds: Bounds, onProgress: (progress: number) => void, onFatalError: (error: Error) => void) {
let packInfo = await Mapbox.offlineManagerLegacy.getPack(zone);
console.log(bounds);
if(packInfo && (packInfo as any).pack.state != Mapbox.OfflinePackDownloadState.Complete) {
// Download was interrupted, remove it and download again
await removeAllPack();
packInfo = undefined;
}
if (!packInfo) {
await Mapbox.offlineManagerLegacy.createPack({
name: zone,
styleURL: Mapbox.StyleURL.SatelliteStreet,
minZoom: 14,
maxZoom: 20, // Maximum zoom level is 22, but 17 is the practical limit for certain zones. In USA and France, zoom level 22 is available.
bounds: [ // [SW, NE]
[bounds.sw[0], bounds.sw[1]],
[bounds.ne[0], bounds.ne[1]],
]}
);
checkPackDownloading(zone, onProgress, onFatalError);
} else {
console.log("Zone already downloaded", zone);
onProgress(100);
}
}
For those who'd like an example in native kotlin code, here's a repo I made to understand how the library works:
Many bugs can occur, notably due to the pixel ratio or the coordinates of your bounding box. And don't forget the limit of 6,000 tiles per region... In some regions, satellite image quality doesn't increase after zoom level 17, but you should be able to download tiles at zoom level 22 everywhere.
To test on your device, I recommend that you try running a native code if the react native code doesn't work. This code has not been tested on iOS.
someone have a solution to estimate download size to ask the user before download please? i tried many different way but nothing work correctly, the estimation are wrong 80% of the time and the rnmapbox module don't implemente the function to calculate
Hey @djangoamidala @Leadola1 @sampathkumarch @M4thi4s — sorry folks, I got a bit caught up and couldn’t follow up on the issue earlier. the problem was in rbmapbox itself — I had to update some of its internal modules to fix it. I’m putting together a PR now, and once it’s accepted, the fix will be included in the next release.
@sahni01 Any updates?
I'm closing this now, feel free to open this In a new issue if you can still reproduce it, or it's still relevant