UNPKG

expo-osm-sdk

Version:

OpenStreetMap component for React Native with Expo

510 lines 23.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.OSMView = exports.TILE_CONFIGS = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importStar(require("react")); const react_native_1 = require("react-native"); const expo_modules_core_1 = require("expo-modules-core"); const types_1 = require("../types"); const coordinate_1 = require("../utils/coordinate"); // Get native view manager and native module let NativeOSMView = null; let NativeOSMModule = null; let isNativeModuleAvailable = false; try { // Modern Expo modules pattern - get the view component directly from the module const ExpoOsmSdkModule = (0, expo_modules_core_1.requireNativeModule)('ExpoOsmSdk'); NativeOSMModule = ExpoOsmSdkModule; // For modern Expo modules, the view is available as a component // Try to get the view component - it might be available as a property or through requireNativeViewManager try { NativeOSMView = (0, expo_modules_core_1.requireNativeViewManager)('ExpoOsmSdk'); } catch (viewError) { console.log('📱 Using module-based view instead of separate view manager'); // If requireNativeViewManager fails, we'll use the module's view component // This will be handled in the component rendering } // More robust native module detection // Check if we're in a proper native environment const hasExpoModules = !!global.ExpoModules; // Better Expo Go detection - check for development client vs Expo Go // In development builds, Constants.executionEnvironment will be different let isExpoGo = false; try { const Constants = require('expo-constants').default; // In Expo Go: executionEnvironment is 'expoGo' // In development builds: executionEnvironment is 'development' or similar isExpoGo = Constants.executionEnvironment === 'expoGo'; } catch { // Fallback to old detection if Constants is not available isExpoGo = !!global.expo; } const isWeb = react_native_1.Platform.OS === 'web'; // Module is available if: // 1. We have the native module // 2. We're NOT in Expo Go // 3. We're NOT on web isNativeModuleAvailable = !!NativeOSMModule && !isExpoGo && !isWeb; console.log('Enhanced native module detection:', { hasExpoModules, hasNativeModule: !!NativeOSMModule, hasNativeView: !!NativeOSMView, isExpoGo, isNativeModuleAvailable, isWeb, platform: react_native_1.Platform.OS }); } catch (error) { // Native module not available (e.g., in Expo Go or web) console.warn('ExpoOsmSdk native module not available:', error); isNativeModuleAvailable = false; } // Using imported validateCoordinate from utils exports.TILE_CONFIGS = { raster: { name: 'OpenStreetMap Raster', type: 'Raster', url: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png' }, vector: { name: 'Carto', type: 'Vector', url: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json', // Backup URL in case the primary fails backupUrl: 'https://maps.tilehosting.com/styles/basic/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL' } }; const OSMView = (0, react_1.forwardRef)(({ style, initialCenter = { latitude: 0, longitude: 0 }, initialZoom = 10, tileServerUrl = types_1.DEFAULT_CONFIG.tileServerUrl, // Use vector tiles by default styleUrl = types_1.DEFAULT_CONFIG.styleUrl, // Add styleUrl support markers = [], showUserLocation = false, followUserLocation = false, onMapReady, onRegionChange, onMarkerPress, onPress, onUserLocationChange, }, ref) => { const nativeViewRef = react_1.default.useRef(null); // Add debugging for tile URL changes react_1.default.useEffect(() => { console.log('🔧 OSMView: tileServerUrl prop changed:', { tileServerUrl, isVector: tileServerUrl ? (0, types_1.isVectorTileUrl)(tileServerUrl) : false, timestamp: new Date().toISOString() }); // Check if we're actually passing this to native if (nativeViewRef.current) { console.log('📱 OSMView: Native view ref exists, should trigger native update'); } else { console.log('⚠️ OSMView: Native view ref is null, native update may not happen'); } }, [tileServerUrl]); // Add debugging for when native view is ready react_1.default.useEffect(() => { if (nativeViewRef.current) { console.log('✅ OSMView: Native view ref initialized'); } }, []); // Expose imperative methods via ref (0, react_1.useImperativeHandle)(ref, () => ({ zoomIn: async () => { if (isNativeModuleAvailable && NativeOSMModule) { console.log('🔍 Calling native zoomIn'); try { await NativeOSMModule.zoomIn(); console.log('✅ Zoom In successful'); } catch (error) { console.error('❌ Zoom In failed:', error); throw error; } } }, zoomOut: async () => { if (isNativeModuleAvailable && NativeOSMModule) { console.log('🔍 Calling native zoomOut'); try { await NativeOSMModule.zoomOut(); console.log('✅ Zoom Out successful'); } catch (error) { console.error('❌ Zoom Out failed:', error); throw error; } } }, setZoom: async (zoom) => { if (isNativeModuleAvailable && NativeOSMModule) { console.log('🔍 Calling native setZoom:', zoom); try { await NativeOSMModule.setZoom(zoom); console.log('✅ Set Zoom successful'); } catch (error) { console.error('❌ Set Zoom failed:', error); throw error; } } }, animateToLocation: async (latitude, longitude, zoom = initialZoom) => { if (isNativeModuleAvailable && NativeOSMModule) { console.log('📍 Calling native animateToLocation:', latitude, longitude, zoom); try { await NativeOSMModule.animateToLocation(latitude, longitude, zoom); console.log('✅ Animate to location successful'); } catch (error) { console.error('❌ Location animation failed:', error); throw error; } } }, getCurrentLocation: async () => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for getCurrentLocation'); throw new Error('Native module not available'); } console.log('📍 Calling native getCurrentLocation'); try { // Wait for view to be ready before calling location methods const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for getCurrentLocation'); throw new Error('OSM view not ready'); } const location = await NativeOSMModule.getCurrentLocation(); console.log('✅ Get current location successful:', location); return location; } catch (error) { console.error('❌ Get location failed:', error); throw error; } }, startLocationTracking: async () => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for startLocationTracking'); throw new Error('Native module not available'); } console.log('📍 Calling native startLocationTracking'); try { // Wait for view to be ready before calling location methods const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for startLocationTracking'); throw new Error('OSM view not ready'); } await NativeOSMModule.startLocationTracking(); console.log('✅ Start location tracking successful'); } catch (error) { console.error('❌ Start location tracking failed:', error); throw error; } }, stopLocationTracking: async () => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for stopLocationTracking'); throw new Error('Native module not available'); } console.log('📍 Calling native stopLocationTracking'); try { // Wait for view to be ready before calling location methods const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for stopLocationTracking'); throw new Error('OSM view not ready'); } await NativeOSMModule.stopLocationTracking(); console.log('✅ Stop location tracking successful'); } catch (error) { console.error('❌ Stop location tracking failed:', error); throw error; } }, waitForLocation: async (timeoutSeconds) => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for waitForLocation'); throw new Error('Native module not available'); } console.log(`📍 Calling native waitForLocation with timeout: ${timeoutSeconds}s`); try { // Wait for view to be ready before calling location methods const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for waitForLocation'); throw new Error('OSM view not ready'); } const location = await NativeOSMModule.waitForLocation(timeoutSeconds); console.log('✅ Wait for location successful:', location); return location; } catch (error) { console.error('❌ Wait for location failed:', error); throw error; } }, // Route display methods for OSRM integration displayRoute: async (coordinates, options = {}) => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for displayRoute'); throw new Error('Native module not available'); } console.log(`🛣️ Calling native displayRoute with ${coordinates.length} coordinates`); try { // Wait for view to be ready const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for displayRoute'); throw new Error('OSM view not ready'); } await NativeOSMModule.displayRoute(coordinates, options); console.log('✅ Display route successful'); } catch (error) { console.error('❌ Display route failed:', error); throw error; } }, clearRoute: async () => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for clearRoute'); throw new Error('Native module not available'); } console.log('🗑️ Calling native clearRoute'); try { // Wait for view to be ready const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for clearRoute'); throw new Error('OSM view not ready'); } await NativeOSMModule.clearRoute(); console.log('✅ Clear route successful'); } catch (error) { console.error('❌ Clear route failed:', error); throw error; } }, fitRouteInView: async (coordinates, padding = 50) => { if (!isNativeModuleAvailable || !NativeOSMModule) { console.error('❌ Native module not available for fitRouteInView'); throw new Error('Native module not available'); } console.log(`📍 Calling native fitRouteInView with ${coordinates.length} coordinates`); try { // Wait for view to be ready const isReady = await waitForViewReady(); if (!isReady) { console.error('❌ View not ready for fitRouteInView'); throw new Error('OSM view not ready'); } await NativeOSMModule.fitRouteInView(coordinates, padding); console.log('✅ Fit route in view successful'); } catch (error) { console.error('❌ Fit route in view failed:', error); throw error; } }, }), [initialZoom, isNativeModuleAvailable]); // Helper function to wait for view to be ready const waitForViewReady = async (maxWaitTime = 10000) => { if (!isNativeModuleAvailable || !NativeOSMModule) { return false; } console.log('⏳ Waiting for OSM view to be ready...'); const startTime = Date.now(); while (Date.now() - startTime < maxWaitTime) { try { const isReady = await NativeOSMModule.isViewReady(); if (isReady) { console.log('✅ OSM view is ready'); return true; } // Wait a bit before checking again await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { console.warn('⚠️ Error checking view readiness:', error); // If isViewReady is not available, assume view is ready if module is available return isNativeModuleAvailable; } } console.warn('⚠️ Timeout waiting for OSM view to be ready'); return false; }; // Validation try { (0, coordinate_1.validateCoordinate)(initialCenter); } catch (error) { throw new Error(`initialCenter: ${error instanceof Error ? error.message : error}`); } const validateZoom = (zoom, propName) => { if (typeof zoom !== 'number' || zoom < 1 || zoom > 20) { throw new Error(`${propName} must be a number between 1 and 20`); } }; validateZoom(initialZoom, 'initialZoom'); if (__DEV__) { try { if (markers && Array.isArray(markers)) { markers.forEach((marker, index) => { try { (0, coordinate_1.validateCoordinate)(marker.coordinate); } catch (error) { throw new Error(`markers[${index}].coordinate: ${error instanceof Error ? error.message : error}`); } }); } } catch (error) { console.error('OSMView validation error:', error); } } // Event handlers for native view const handleMapReady = () => { onMapReady?.(); }; const handleRegionChange = (event) => { const region = event.nativeEvent; onRegionChange?.(region); }; const handleMarkerPress = (event) => { const { markerId, coordinate } = event.nativeEvent; onMarkerPress?.(markerId, coordinate || { latitude: 0, longitude: 0 }); }; const handlePress = (event) => { const { coordinate } = event.nativeEvent; onPress?.(coordinate); }; const handleUserLocationChange = (event) => { const { latitude, longitude } = event.nativeEvent; onUserLocationChange?.({ latitude, longitude }); }; // Check if native module is available if (!isNativeModuleAvailable) { return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style], testID: "osm-view-fallback", children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.fallbackContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fallbackTitle, children: "\uD83D\uDCCD OpenStreetMap View" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fallbackText, children: react_native_1.Platform.OS === 'web' ? 'Web platform requires a different map implementation' : 'This app requires a development build to display maps' }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fallbackSubtext, children: react_native_1.Platform.OS === 'web' ? 'Consider using react-native-web compatible map libraries for web support' : 'Expo Go does not support custom native modules. Please create a development build.' }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.coordinateInfo, children: [(0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.coordinateText, children: ["\uD83D\uDCCD Center: ", initialCenter.latitude.toFixed(4), ", ", initialCenter.longitude.toFixed(4)] }), (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.coordinateText, children: ["\uD83D\uDD0D Zoom: ", initialZoom] })] })] }) })); } // If we don't have a native view manager but have the module, try to create the view directly if (!NativeOSMView && NativeOSMModule) { console.warn('⚠️ NativeOSMView not available, but module exists. This might indicate a module definition issue.'); console.log('🔧 Available module methods:', Object.getOwnPropertyNames(NativeOSMModule)); return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style], testID: "osm-view-debug", children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.fallbackContainer, children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fallbackTitle, children: "\uD83D\uDD27 Debug Mode" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fallbackText, children: "Native module loaded but view component not available." }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fallbackSubtext, children: "This indicates the View definition in the native module may not be working correctly." }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.coordinateInfo, children: [(0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.coordinateText, children: ["Module: ", NativeOSMModule ? '✅ Available' : '❌ Missing'] }), (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: styles.coordinateText, children: ["View: ", NativeOSMView ? '✅ Available' : '❌ Missing'] })] })] }) })); } return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style], testID: "osm-view", children: (0, jsx_runtime_1.jsx)(NativeOSMView, { ref: nativeViewRef, style: styles.map, initialCenter: initialCenter, initialZoom: initialZoom, tileServerUrl: tileServerUrl, styleUrl: styleUrl, markers: markers, onMapReady: handleMapReady, onRegionChange: handleRegionChange, onMarkerPress: handleMarkerPress, onPress: handlePress, showUserLocation: showUserLocation, followUserLocation: followUserLocation, onUserLocationChange: handleUserLocationChange }) })); }); exports.OSMView = OSMView; OSMView.displayName = 'OSMView'; exports.default = OSMView; const styles = react_native_1.StyleSheet.create({ container: { flex: 1, }, map: { flex: 1, }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f8d7da', borderColor: '#f5c6cb', borderWidth: 1, borderRadius: 4, padding: 16, }, errorText: { color: '#721c24', textAlign: 'center', fontSize: 14, }, fallbackContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f0f8ff', borderColor: '#b3d9ff', borderWidth: 2, borderRadius: 12, padding: 20, margin: 10, }, fallbackTitle: { fontSize: 24, fontWeight: 'bold', color: '#1a365d', marginBottom: 16, textAlign: 'center', }, fallbackText: { fontSize: 16, color: '#2d3748', textAlign: 'center', marginBottom: 12, lineHeight: 22, }, fallbackSubtext: { fontSize: 14, color: '#718096', textAlign: 'center', marginBottom: 20, fontStyle: 'italic', lineHeight: 20, }, coordinateInfo: { backgroundColor: '#edf2f7', padding: 12, borderRadius: 8, minWidth: 200, }, coordinateText: { fontSize: 14, color: '#4a5568', textAlign: 'center', marginBottom: 4, fontFamily: 'monospace', }, }); //# sourceMappingURL=OSMView.js.map