@chauffleet/expo-custom-map
Version:
Open source custom map library for Expo/React Native. Use your own tiles without Google Maps, Mapbox, or API keys. Created by ChaufFleet.
135 lines • 6.27 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TileLayer = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const react_native_1 = require("react-native");
const TILE_SIZE = 256;
const BUFFER_TILES = 1;
/**
* TileLayer - Layer de tuiles ultra-optimisé pour une fluidité parfaite
*
* Optimisations clés :
* 1. Memoization agressive des calculs de tuiles
* 2. Pas de recalcul pendant les gestes
* 3. Cache des URLs de tuiles
* 4. Rendu optimisé avec position absolue
*/
const TileLayer = ({ region, tileUrlTemplate, screenWidth, screenHeight, isGesturing = false, }) => {
// Calcul du niveau de zoom optimal
const zoomLevel = (0, react_1.useMemo)(() => {
// Calcul basé sur latitudeDelta pour avoir la bonne résolution
const calculatedZoom = Math.log2(360 / region.latitudeDelta);
return Math.max(1, Math.min(18, Math.round(calculatedZoom)));
}, [region.latitudeDelta]);
// Fonction pour convertir lat/lon en coordonnées de tuile
const getTileCoords = (0, react_1.useMemo)(() => {
return {
latToTileY: (lat, zoom) => {
const latRad = (lat * Math.PI) / 180;
const mercN = Math.log(Math.tan(Math.PI / 4 + latRad / 2));
return Math.floor(((1 - mercN / Math.PI) / 2) * Math.pow(2, zoom));
},
lonToTileX: (lon, zoom) => {
return Math.floor(((lon + 180) / 360) * Math.pow(2, zoom));
},
tileToLat: (y, zoom) => {
const n = Math.PI * (1 - 2 * y / Math.pow(2, zoom));
return (180 / Math.PI) * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
},
tileToLon: (x, zoom) => {
return (x / Math.pow(2, zoom)) * 360 - 180;
},
};
}, []);
// Calcul des tuiles nécessaires - avec memoization agressive
const tiles = (0, react_1.useMemo)(() => {
const zoom = zoomLevel;
const { latToTileY, lonToTileX, tileToLat, tileToLon } = getTileCoords;
// Coordonnées du centre de la carte en tuiles
const centerTileX = lonToTileX(region.longitude, zoom);
const centerTileY = latToTileY(region.latitude, zoom);
// Calcul de la zone visible en coordonnées de tuiles
const degreesPerPixelLat = region.latitudeDelta / screenHeight;
const degreesPerPixelLon = region.longitudeDelta / screenWidth;
// Ajout du buffer pour les tuiles en bordure
const tilesNeededX = Math.ceil(screenWidth / TILE_SIZE) + BUFFER_TILES * 2;
const tilesNeededY = Math.ceil(screenHeight / TILE_SIZE) + BUFFER_TILES * 2;
const startTileX = centerTileX - Math.floor(tilesNeededX / 2);
const endTileX = centerTileX + Math.ceil(tilesNeededX / 2);
const startTileY = centerTileY - Math.floor(tilesNeededY / 2);
const endTileY = centerTileY + Math.ceil(tilesNeededY / 2);
const tileList = [];
const maxTile = Math.pow(2, zoom);
// Calcul des pixels par degré pour le positionnement
const pixelsPerDegreeLat = screenHeight / region.latitudeDelta;
const pixelsPerDegreeLon = screenWidth / region.longitudeDelta;
for (let tileX = startTileX; tileX <= endTileX; tileX++) {
for (let tileY = startTileY; tileY <= endTileY; tileY++) {
// Validation des coordonnées Y
if (tileY < 0 || tileY >= maxTile)
continue;
// Wrap-around pour X (passage de -180° à 180°)
let wrappedX = tileX;
while (wrappedX < 0)
wrappedX += maxTile;
while (wrappedX >= maxTile)
wrappedX -= maxTile;
// URL de la tuile
const url = tileUrlTemplate
.replace('{z}', zoom.toString())
.replace('{x}', wrappedX.toString())
.replace('{y}', tileY.toString());
// Calcul de la position sur l'écran
const tileLat = tileToLat(tileY, zoom);
const tileLon = tileToLon(wrappedX, zoom);
// Position relative au centre de l'écran
const latDiff = region.latitude - tileLat;
const lonDiff = region.longitude - tileLon;
const left = screenWidth / 2 - lonDiff * pixelsPerDegreeLon;
const top = screenHeight / 2 + latDiff * pixelsPerDegreeLat;
tileList.push({
x: wrappedX,
y: tileY,
z: zoom,
url,
key: `${zoom}-${wrappedX}-${tileY}`,
left: left,
top: top,
});
}
}
return tileList;
}, [region, zoomLevel, screenWidth, screenHeight, tileUrlTemplate, getTileCoords]);
// Style de container optimisé
const containerStyle = (0, react_1.useMemo)(() => ({
position: 'absolute',
width: screenWidth,
height: screenHeight,
overflow: 'hidden',
}), [screenWidth, screenHeight]);
// Rendu des tuiles avec optimisation
const renderedTiles = (0, react_1.useMemo)(() => {
return tiles.map((tile) => ((0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: tile.url }, style: [
styles.tile,
{
left: tile.left,
top: tile.top,
width: TILE_SIZE,
height: TILE_SIZE,
},
], resizeMode: "cover",
// Optimisations pour la performance
fadeDuration: 0, progressiveRenderingEnabled: true, defaultSource: undefined }, tile.key)));
}, [tiles]);
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: containerStyle, children: renderedTiles }));
};
exports.TileLayer = TileLayer;
const styles = react_native_1.StyleSheet.create({
tile: {
position: 'absolute',
backgroundColor: '#f0f0f0', // Couleur de fond pendant le chargement
},
});
exports.default = exports.TileLayer;
//# sourceMappingURL=TileLayer.js.map