UNPKG

@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
"use strict"; 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