expo-osm-sdk
Version:
OpenStreetMap component for React Native with Expo
669 lines • 30.8 kB
JavaScript
;
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 = [], circles = [], polylines = [], polygons = [], showUserLocation = false, followUserLocation = false, showsCompass = false, showsScale = false, showsZoomControls = false, rotateEnabled = true, scrollEnabled = true, zoomEnabled = true, pitchEnabled = true, 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;
}
},
// Camera orientation methods
setPitch: async (pitch) => {
if (!isNativeModuleAvailable || !NativeOSMModule) {
console.error('❌ Native module not available for setPitch');
throw new Error('Native module not available');
}
console.log(`📐 Calling native setPitch: ${pitch}`);
try {
const isReady = await waitForViewReady();
if (!isReady) {
console.error('❌ View not ready for setPitch');
throw new Error('OSM view not ready');
}
await NativeOSMModule.setPitch(pitch);
console.log('✅ Set pitch successful');
}
catch (error) {
console.error('❌ Set pitch failed:', error);
throw error;
}
},
setBearing: async (bearing) => {
if (!isNativeModuleAvailable || !NativeOSMModule) {
console.error('❌ Native module not available for setBearing');
throw new Error('Native module not available');
}
console.log(`🧭 Calling native setBearing: ${bearing}`);
try {
const isReady = await waitForViewReady();
if (!isReady) {
console.error('❌ View not ready for setBearing');
throw new Error('OSM view not ready');
}
await NativeOSMModule.setBearing(bearing);
console.log('✅ Set bearing successful');
}
catch (error) {
console.error('❌ Set bearing failed:', error);
throw error;
}
},
getPitch: async () => {
if (!isNativeModuleAvailable || !NativeOSMModule) {
console.error('❌ Native module not available for getPitch');
throw new Error('Native module not available');
}
console.log('📐 Calling native getPitch');
try {
const isReady = await waitForViewReady();
if (!isReady) {
console.error('❌ View not ready for getPitch');
throw new Error('OSM view not ready');
}
const pitch = await NativeOSMModule.getPitch();
console.log('✅ Get pitch successful:', pitch);
return pitch;
}
catch (error) {
console.error('❌ Get pitch failed:', error);
throw error;
}
},
getBearing: async () => {
if (!isNativeModuleAvailable || !NativeOSMModule) {
console.error('❌ Native module not available for getBearing');
throw new Error('Native module not available');
}
console.log('🧭 Calling native getBearing');
try {
const isReady = await waitForViewReady();
if (!isReady) {
console.error('❌ View not ready for getBearing');
throw new Error('OSM view not ready');
}
const bearing = await NativeOSMModule.getBearing();
console.log('✅ Get bearing successful:', bearing);
return bearing;
}
catch (error) {
console.error('❌ Get bearing failed:', error);
throw error;
}
},
animateCamera: async (options) => {
if (!isNativeModuleAvailable || !NativeOSMModule) {
console.error('❌ Native module not available for animateCamera');
throw new Error('Native module not available');
}
console.log('🎥 Calling native animateCamera:', options);
try {
const isReady = await waitForViewReady();
if (!isReady) {
console.error('❌ View not ready for animateCamera');
throw new Error('OSM view not ready');
}
await NativeOSMModule.animateCamera(options);
console.log('✅ Animate camera successful');
}
catch (error) {
console.error('❌ Animate camera 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);
}
// Validate circles coordinates
try {
if (circles && Array.isArray(circles)) {
circles.forEach((circle, index) => {
try {
(0, coordinate_1.validateCoordinate)(circle.center);
}
catch (error) {
console.error(`OSMView validation error: circles[${index}].center: ${error instanceof Error ? error.message : error}`);
}
});
}
}
catch (error) {
console.error('OSMView circles validation error:', error);
}
// Validate polylines coordinates
try {
if (polylines && Array.isArray(polylines)) {
polylines.forEach((polyline, polylineIndex) => {
if (polyline.coordinates && Array.isArray(polyline.coordinates)) {
polyline.coordinates.forEach((coordinate, coordIndex) => {
try {
(0, coordinate_1.validateCoordinate)(coordinate);
}
catch (error) {
console.error(`OSMView validation error: polylines[${polylineIndex}].coordinates[${coordIndex}]: ${error instanceof Error ? error.message : error}`);
}
});
}
});
}
}
catch (error) {
console.error('OSMView polylines validation error:', error);
}
// Validate polygons coordinates
try {
if (polygons && Array.isArray(polygons)) {
polygons.forEach((polygon, polygonIndex) => {
if (polygon.coordinates && Array.isArray(polygon.coordinates)) {
polygon.coordinates.forEach((coordinate, coordIndex) => {
try {
(0, coordinate_1.validateCoordinate)(coordinate);
}
catch (error) {
console.error(`OSMView validation error: polygons[${polygonIndex}].coordinates[${coordIndex}]: ${error instanceof Error ? error.message : error}`);
}
});
}
});
}
}
catch (error) {
console.error('OSMView polygons 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, circles: circles, polylines: polylines, polygons: polygons, onMapReady: handleMapReady, onRegionChange: handleRegionChange, onMarkerPress: handleMarkerPress, onPress: handlePress, showUserLocation: showUserLocation, followUserLocation: followUserLocation, showsCompass: showsCompass, showsScale: showsScale, showsZoomControls: showsZoomControls, rotateEnabled: rotateEnabled, scrollEnabled: scrollEnabled, zoomEnabled: zoomEnabled, pitchEnabled: pitchEnabled, 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