I'm using @rnmapbox/maps, and would like to be able to show a full map of the world like below on a mobile screen (i.e., with all countries shown at once). I don't particularly mind what the height to width ratio is, as long as the whole map fits on the screen. But it seems that even at minimum zoom (0), the map gets cut off both horizontally and vertically (see smaller greenish map). My understanding is that this might be a hard limitation, that zoom level 0 is by definition a 512x512 tile, and if you don't have 512px to play with, you're going to get cropped. The average mobile screen is more like 400px. But that seems like a crazy limitation, a mapping library that can't actually show you a world map on the majority of devices in the world.
This is a simple version of my code, as you can see I'm trying everything from zoomLevel to bounds to padding:
<MapView
style={{ flex: 1 }}
projection="mercator"
styleJSON={JSON.stringify(mapStyleJson, null, 0) || undefined}
rotateEnabled={false}
scaleBarEnabled={false}
pitchEnabled={false}
compassEnabled={false}
zoomEnabled={true}
attributionPosition={{
right: 0,
top: 8,
}}
>
<MapboxGL.Camera
minZoomLevel={0}
maxZoomLevel={20}
padding={{
paddingLeft: 0,
paddingRight: 0,
paddingTop: 0,
paddingBottom: 90,
}}
bounds={{
ne: [180, 85],
sw: [-180, -85],
}}
zoomLevel={0}
/>
</MapView>
This is a related question, although it's 7 years old now and not for mobile or the same library.
What I want:
What I get:
OK, so it's true: in Mapbox, a zoom level of 0 equates to a 512x512px tile. There is a way around this if your screen is less than this size. My code below is from my actual app so it's a little convoluted, but basically the answer is that you need a transform: scale
on the immediate parent of the map (to get it to the right size), then also an offset with transform: translate
to re-center the map. It seems like a hack, but it works fine for me in production on iOS, Android and web.
const TILE_SIZE = 512 // Mapbox defines scale 0 as a 512x512px tile
// SCALING
const mapWidth = width - borderWidth * 2
const scaledRatio = (1 / TILE_SIZE) * mapWidth
const displayScale = shouldScale ? scaledRatio : 1
const offset = shouldScale ? (TILE_SIZE - mapWidth) / 2 : 0
const scaleStyle = {
height: shouldScale ? TILE_SIZE : height,
width: shouldScale ? TILE_SIZE : mapWidth,
transform: [{ scale: displayScale }],
}
const transformStyle: ViewStyle = {
transform: [{ translateX: -offset }, { translateY: -offset }],
}
return (
<>
<Box
width={width}
height={height}
pointerEvents="box-none"
borderWidth={borderWidth}
{...boxProps}
>
<Box
overflow={'hidden'}
width={width}
height={height}
position={'relative'}
pointerEvents="box-none"
>
{!!mapStyleJson && (
<Box style={transformStyle} pointerEvents="box-none">
<Box style={scaleStyle} pointerEvents="box-none">
<MapView
ref={mapRef}
style={{ flex: 1 }}
{...}