diff --git a/README.md b/README.md index 126027d..4f96c26 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,36 @@ Event driven communication between React Native and Native layers(iOS, android). android: android/src/main/java/com/meridianmaps/MapViewFragment.java - ```kt - private void sendEvent(String eventName, @androidx.annotation.Nullable com.facebook.react.bridge.WritableMap params) +private void sendEvent(String eventName, @androidx.annotation.Nullable com.facebook.react.bridge.WritableMap params) ``` iOS: ios/MeridianCustom/MMEventEmitter.m - -```swift -swift - (void)emitCustomEvent: (NSString *)eventName body: (NSDictionary *)body +```objc +- (void)emitCustomEvent: (NSString *)eventName body: (NSDictionary *)body ``` +#### booth simulated navigation - +booth navigation is only possible for certain routes(GPS coordinates can be configured in the [Edit console](edit.meridianapp.com)) +custom `startRouteToPlacemark` method is implemented to allow navigation by booth id + +android: android/src/main/java/com/meridianmaps/MeridianMapViewManager.kt +```kt +fun startRouteToPlacemark(placemarkId: String) +``` +iOS: ios/MeridianMapViewManager.m +```objc +- (void)startRouteToPlacemarkWithID:(NSString *)placemarkID +``` +##### debug +booth id logging can be achieved with +android: android/src/main/java/com/meridianmaps/MapViewFragment.java' +```java +public boolean onMarkerSelect(Marker marker) +``` +iOS: ios/MeridianCustom/CustomMapViewController.m +```objc +- (void)mapView:(MRMapView *)mapView didSelectAnnotationView:(MRAnnotationView *)view +``` https://github.com/user-attachments/assets/83cb79f4-0718-478a-82f3-940debb7f2f9 @@ -47,3 +69,10 @@ https://github.com/user-attachments/assets/1070d682-f7ef-41c3-bede-9f008677654a ### android: 1. if map has multiple floors default floor doesn't render [workaround: navigate by booth id and then manually navigate to the desired floor] 2. search icon press handling (UI breaks on repeated press) +3. if there is no simulated navigation - `startRouteToPlacemark` will try to open the search bar (which causes bugs on iOS) + +workaround with navigating to the matching floor first works but takes a long time +ios/MeridianMapViewManager.m +```objc +self getAllPlacemarksFromAllFloors:^(NSArray *placemarks, NSError *error) +``` \ No newline at end of file diff --git a/react-native-meridian-maps/API.md b/react-native-meridian-maps/API.md new file mode 100644 index 0000000..df776ad --- /dev/null +++ b/react-native-meridian-maps/API.md @@ -0,0 +1,365 @@ +# API Reference + +This document provides detailed API reference for all components, interfaces, and methods available in react-native-meridian-maps. + +## Table of Contents + +- [Components](#components) + - [MeridianMapView](#meridianmapview) +- [Interfaces](#interfaces) + - [MeridianMapViewProps](#meridianmapviewprops) + - [MeridianMapViewComponentRef](#meridianmapviewcomponentref) + - [MeridianMapsInterface](#meridianmapsinterface) +- [Modules](#modules) + - [MeridianMaps](#meridianmaps) +- [Utility Functions](#utility-functions) + - [isAvailable](#isavailable) +- [Event Types](#event-types) +- [Error Handling](#error-handling) + +## Components + +### MeridianMapView + +The primary component for displaying Meridian indoor maps with real-time location tracking and navigation capabilities. + +#### Usage + +```typescript +import { MeridianMapView } from 'react-native-meridian-maps'; + + console.log('Map loaded')} +/> +``` + +#### Props + +See [MeridianMapViewProps](#meridianmapviewprops) for detailed prop definitions. + +#### Ref Methods + +Access component methods using a ref: + +```typescript +const mapRef = useRef(null); + +// Trigger map update +mapRef.current?.triggerUpdate(); + +// Start navigation to placemark +mapRef.current?.startRoute('placemark-id'); +``` + +## Interfaces + +### MeridianMapViewProps + +```typescript +interface MeridianMapViewProps { + // Required Props + appId: string; // Meridian application ID + mapId: string; // Map identifier + appToken: string; // Authentication token + + // Optional Props + style?: ViewStyle; // Component styling + showLocationUpdates?: boolean; // Enable location tracking (default: true) + + // Event Handlers + onMapLoadStart?: () => void; + onMapLoadFinish?: () => void; + onMapLoadFail?: (error: ErrorEvent) => void; + onLocationUpdated?: (location: LocationEvent) => void; + onMarkerSelect?: (marker: MarkerEvent) => void; + onMarkerDeselect?: (marker: MarkerEvent) => void; + onMapTransformChange?: (transform: TransformEvent) => void; + onOrientationUpdated?: (orientation: OrientationEvent) => void; + onSearchActivityStarted?: (search: SearchEvent) => void; + + // Navigation Events + onDirectionsReroute?: (route: DirectionsEvent) => void; + onDirectionsClick?: (directions: DirectionsEvent) => void; + onDirectionsStart?: (directions: DirectionsEvent) => void; + onRouteStepIndexChange?: (step: RouteStepEvent) => void; + onDirectionsClosed?: () => void; + onDirectionsError?: (error: DirectionsErrorEvent) => void; + onUseAccessiblePathsChange?: (accessible: AccessibilityEvent) => void; + onDirectionsCalculated?: (directions: DirectionsEvent) => void; + onDirectionsRequestComplete?: (request: DirectionsRequestEvent) => void; + onDirectionsRequestError?: (error: DirectionsRequestErrorEvent) => void; + onDirectionsRequestCanceled?: () => void; + + // Marker Events + markerForSelectedMarker?: (marker: MarkerEvent) => void; + onCalloutClick?: (callout: CalloutEvent) => void; + + // Error Handling + onError?: (error: ErrorEvent) => void; +} +``` + +### MeridianMapViewComponentRef + +Interface for component ref methods: + +```typescript +interface MeridianMapViewComponentRef { + /** + * Trigger a manual update of the map component + * Useful for refreshing the map after configuration changes + */ + triggerUpdate: () => void; + + /** + * Start navigation to a specific placemark + * @param placemarkID - The unique identifier of the destination placemark + */ + startRoute: (placemarkID: string) => void; +} +``` + +### MeridianMapsInterface + +Interface for the native module: + +```typescript +interface MeridianMapsInterface { + /** + * Open map in native activity/view controller + * @param appId - Optional app ID override + * @param mapId - Optional map ID override + * @returns Promise resolving to operation result + */ + openMap(appId?: string, mapId?: string): Promise; + + /** + * Open test activity for development/debugging + * @returns Promise resolving to test result + */ + openTestActivity(): Promise; +} +``` + +## Modules + +### MeridianMaps + +The native module providing low-level SDK functionality: + +```typescript +import { MeridianMaps } from 'react-native-meridian-maps'; + +// Open native map interface +const result = await MeridianMaps.openMap('app-id', 'map-id'); + +// Open test interface +const testResult = await MeridianMaps.openTestActivity(); +``` + +## Utility Functions + +### isAvailable + +Check if the Meridian SDK is properly installed and available: + +```typescript +import { isAvailable } from 'react-native-meridian-maps'; + +const checkSDK = async () => { + try { + const available = await isAvailable(); + if (available) { + console.log('✅ Meridian SDK is ready'); + // Initialize your map + } else { + console.log('❌ Meridian SDK not available'); + // Show error message or fallback UI + } + } catch (error) { + console.error('Error checking SDK:', error); + } +}; +``` + +#### Returns + +- `Promise` - `true` if SDK is available, `false` otherwise + +#### What it checks + +1. Native component registration in UIManager +2. Native module availability +3. Platform-specific SDK initialization +4. View manager configuration + +## Event Types + +### LocationEvent + +```typescript +interface LocationEvent { + latitude: number; + longitude: number; + accuracy?: number; + floor?: number; + building?: string; + timestamp: number; +} +``` + +### MarkerEvent + +```typescript +interface MarkerEvent { + id: string; + name?: string; + description?: string; + latitude: number; + longitude: number; + floor?: number; + category?: string; +} +``` + +### DirectionsEvent + +```typescript +interface DirectionsEvent { + routeId: string; + distance: number; + duration: number; + steps: RouteStep[]; + destination: MarkerEvent; +} +``` + +### ErrorEvent + +```typescript +interface ErrorEvent { + code: string; + message: string; + details?: any; + timestamp: number; +} +``` + +### TransformEvent + +```typescript +interface TransformEvent { + zoom: number; + center: { + latitude: number; + longitude: number; + }; + bearing?: number; + tilt?: number; +} +``` + +## Error Handling + +### Common Error Codes + +| Code | Description | Solution | +|------|-------------|----------| +| `SDK_NOT_AVAILABLE` | Meridian SDK not installed | Check installation and rebuild | +| `INVALID_CREDENTIALS` | Invalid app ID, map ID, or token | Verify credentials | +| `NETWORK_ERROR` | Network connectivity issues | Check internet connection | +| `MAP_LOAD_FAILED` | Map failed to load | Check credentials and network | +| `LOCATION_PERMISSION_DENIED` | Location permissions not granted | Request location permissions | +| `NAVIGATION_ERROR` | Navigation/routing error | Check placemark ID and connectivity | +| `FRAGMENT_MANAGER_ERROR` | Android FragmentManager transaction conflict | Delay map initialization or use proper lifecycle management | +| `SDK_CONFIGURE_ERROR` | SDK configure() called multiple times | Implement native layer guards or App-level initialization | + + +## Advanced Usage + +### Custom Event Handling + +```typescript +const AdvancedMapExample = () => { + const [mapState, setMapState] = useState({ + isLoaded: false, + currentLocation: null, + selectedMarker: null, + isNavigating: false, + }); + + const handleLocationUpdate = useCallback((location: LocationEvent) => { + setMapState(prev => ({ + ...prev, + currentLocation: location + })); + + // Custom location processing + if (location.accuracy && location.accuracy > 10) { + console.warn('Low location accuracy:', location.accuracy); + } + }, []); + + const handleMarkerInteraction = useCallback((marker: MarkerEvent) => { + setMapState(prev => ({ + ...prev, + selectedMarker: marker + })); + + // Custom marker logic + Analytics.track('marker_selected', { + markerId: marker.id, + markerName: marker.name + }); + }, []); + + return ( + setMapState(prev => ({ ...prev, isNavigating: true }))} + onDirectionsClosed={() => setMapState(prev => ({ ...prev, isNavigating: false }))} + /> + ); +}; +``` + +### Performance Optimization + +```typescript +const OptimizedMapView = memo(({ appId, mapId, appToken, ...otherProps }) => { + // Memoize event handlers to prevent unnecessary re-renders + const handleLocationUpdate = useCallback((location) => { + // Throttle location updates if needed + throttledLocationUpdate(location); + }, []); + + const handleMapTransformChange = useMemo( + () => debounce((transform) => { + // Handle transform changes with debouncing + console.log('Map transform:', transform); + }, 100), + [] + ); + + return ( + + ); +}); +``` + +--- + +For more examples and implementation details, see the [main README](README.md) and the [example app](example/src/App.tsx). diff --git a/react-native-meridian-maps/README.md b/react-native-meridian-maps/README.md index 03e54df..d37dc18 100644 --- a/react-native-meridian-maps/README.md +++ b/react-native-meridian-maps/README.md @@ -1,23 +1,494 @@ # react-native-meridian-maps -# Installation -``` +A React Native wrapper for the Meridian SDK, enabling indoor mapping and location services in React Native applications. + +## Overview + +react-native-meridian-maps provides React Native components and APIs to integrate Meridian's indoor positioning and mapping capabilities into your mobile applications. The library supports both iOS and Android platforms and offers features like real-time location tracking, turn-by-turn directions, marker interactions, and more. + +## Installation + +```bash npm install react-native-meridian-maps ``` -# How to use -```js +or + +```bash +yarn add react-native-meridian-maps +``` + +### iOS Setup + +1. Navigate to your iOS project directory: + ```bash + cd ios && pod install + ``` + +2. The library includes the Meridian.xcframework, so no additional SDK setup is required. + +### Android Setup + +1. The library includes all necessary Android dependencies and configurations. + +2. Make sure your `android/build.gradle` has the following minimum versions: + ```gradle + compileSdkVersion 34 + minSdkVersion 30 + targetSdkVersion 34 + ``` + +3. Rebuild your project: + ```bash + npx react-native run-android + ``` + +## Quick Start + +```typescript +import React from 'react'; +import { SafeAreaView } from 'react-native'; import { MeridianMapView } from 'react-native-meridian-maps'; export default function App() { return ( - - + + console.log('Map loaded')} + onLocationUpdated={(location) => console.log('Location:', location)} + /> ); } +``` + +## API Reference + +### Components + +#### `MeridianMapView` + +The main component for displaying Meridian maps. + +**Props:** + +| Prop | Type | Required | Description | +|------|------|----------|-------------| +| `appId` | `string` | ✅ | Your Meridian application ID | +| `mapId` | `string` | ✅ | The map ID to display | +| `appToken` | `string` | ✅ | Your Meridian app token | +| `style` | `ViewStyle` | ❌ | Style object for the map container | +| `showLocationUpdates` | `boolean` | ❌ | Enable/disable location updates (default: true) | + +**Event Handlers:** + +| Event | Type | Description | +|-------|------|-------------| +| `onMapLoadStart` | `() => void` | Called when map starts loading | +| `onMapLoadFinish` | `() => void` | Called when map finishes loading | +| `onMapLoadFail` | `(error: any) => void` | Called when map fails to load | +| `onLocationUpdated` | `(location: any) => void` | Called when user location is updated | +| `onMarkerSelect` | `(marker: any) => void` | Called when a marker is selected | +| `onMarkerDeselect` | `(marker: any) => void` | Called when a marker is deselected | +| `onMapTransformChange` | `(transform: any) => void` | Called when map transform changes (zoom, pan) | +| `onOrientationUpdated` | `(orientation: any) => void` | Called when device orientation changes | +| `onDirectionsStart` | `(directions: any) => void` | Called when directions navigation starts | +| `onDirectionsCalculated` | `(directions: any) => void` | Called when route is calculated | +| `onDirectionsClosed` | `() => void` | Called when directions are closed | +| `onDirectionsError` | `(error: any) => void` | Called when directions encounter an error | +| `onDirectionsReroute` | `(route: any) => void` | Called when route is recalculated | +| `onDirectionsClick` | `(directions: any) => void` | Called when directions UI is clicked | +| `onRouteStepIndexChange` | `(stepIndex: any) => void` | Called when current route step changes | +| `onDirectionsRequestComplete` | `(request: any) => void` | Called when directions request completes | +| `onDirectionsRequestError` | `(error: any) => void` | Called when directions request fails | +| `onDirectionsRequestCanceled` | `() => void` | Called when directions request is canceled | +| `onUseAccessiblePathsChange` | `(accessible: any) => void` | Called when accessible paths setting changes | +| `onSearchActivityStarted` | `(search: any) => void` | Called when search activity starts | +| `onCalloutClick` | `(callout: any) => void` | Called when marker callout is clicked | +| `onError` | `(error: any) => void` | Called when a general error occurs | + +**Component Reference:** + +```typescript +interface MeridianMapViewComponentRef { + triggerUpdate: () => void; + startRoute: (placemarkID: string) => void; +} +``` + +Use a ref to access these methods: + +```typescript +import { useRef } from 'react'; + +const mapRef = useRef(null); + +// Trigger a map update +mapRef.current?.triggerUpdate(); + +// Start navigation to a specific placemark +mapRef.current?.startRoute('placemark-id'); +``` + +### Modules + +#### `MeridianMaps` + +The native module providing additional functionality. + +```typescript +import { MeridianMaps } from 'react-native-meridian-maps'; + +// Open map in native activity (Android) or view controller (iOS) +const result = await MeridianMaps.openMap(appId, mapId); + +// Open test activity (development/testing) +const testResult = await MeridianMaps.openTestActivity(); +``` + +### Utility Functions + +#### `isAvailable()` + +Check if the Meridian SDK is available and properly configured. + +```typescript +import { isAvailable } from 'react-native-meridian-maps'; + +const available = await isAvailable(); +if (available) { + // Meridian SDK is ready to use +} else { + // Handle unavailable SDK +} +``` + +## Usage Examples + +### Basic Map with Location Tracking + +```typescript +import React, { useState } from 'react'; +import { View, Text, StyleSheet } from 'react-native'; +import { MeridianMapView } from 'react-native-meridian-maps'; + +export default function MapScreen() { + const [currentLocation, setCurrentLocation] = useState(null); + + return ( + + { + setCurrentLocation(location); + console.log('Current location:', location); + }} + onMapLoadFinish={() => { + console.log('Map ready!'); + }} + /> + {currentLocation && ( + + Location: {JSON.stringify(currentLocation)} + + )} + + ); +} + +const styles = StyleSheet.create({ + container: { flex: 1 }, + map: { flex: 1 }, + locationText: { padding: 10, backgroundColor: 'rgba(0,0,0,0.7)', color: 'white' } +}); +``` + +### Navigation with Turn-by-Turn Directions + +```typescript +import React, { useRef, useState } from 'react'; +import { View, Button, Alert } from 'react-native'; +import { MeridianMapView, type MeridianMapViewComponentRef } from 'react-native-meridian-maps'; + +export default function NavigationScreen() { + const mapRef = useRef(null); + const [isNavigating, setIsNavigating] = useState(false); + + const startNavigation = () => { + const destinationId = 'your-placemark-id'; + mapRef.current?.startRoute(destinationId); + }; + + return ( + + { + setIsNavigating(true); + console.log('Navigation started'); + }} + onDirectionsClosed={() => { + setIsNavigating(false); + console.log('Navigation ended'); + }} + onDirectionsError={(error) => { + Alert.alert('Navigation Error', error.message); + }} + onRouteStepIndexChange={(step) => { + console.log('Route step changed:', step); + }} + /> + + +