Skip to content

Version 0.3.2 Alpha

Release Date: July 30, 2024
Status: Alpha Release
Build Number: 0.3.2


Overview

Version 0.3.2 represents a major milestone in the MINE - Indoor Navigation Engine journey, introducing advanced path finding capabilities that enable real-time route calculation and turn-by-turn navigation within indoor spaces. This release transforms MINE from a map visualization tool into a complete navigation solution.

Stable Alpha Release

This is our most stable alpha release yet, with significant performance improvements and zero critical known issues. Ready for pilot deployments and extensive testing.


Highlights

  • Path Finding Engine


    Advanced A* algorithm with obstacle avoidance and multi-floor support

  • Performance Optimized


    Lightning-fast route calculation with minimal resource consumption

  • Navigation UI


    Complete set of UI components for turn-by-turn navigation

  • Extensible System


    Plugin architecture for custom routing algorithms


New Features

Path Finding Utilities

A comprehensive suite of utilities for route calculation, manipulation, and optimization.

Core Capabilities:

  • ๐ŸŽฏ A* Algorithm Implementation - Efficient shortest path calculation
  • ๐Ÿšง Obstacle Detection - Automatic avoidance of blocked areas
  • ๐Ÿข Multi-Floor Routing - Seamless navigation across building levels
  • โ™ฟ Accessible Routes - Alternative paths for accessibility needs
  • ๐Ÿ”„ Dynamic Rerouting - Real-time route adjustments
  • ๐Ÿ“ Distance Calculation - Accurate distance and time estimates
  • ๐ŸŽจ Route Visualization - Beautiful animated path rendering

Usage Example:

import com.machinestalk.indoornavigationengine.util.PathFindingUtil
import com.machinestalk.indoornavigationengine.models.Location
import com.machinestalk.indoornavigationengine.models.Route

class NavigationController(private val sceneView: MineSceneView) {

    private val pathFinder = PathFindingUtil(sceneView)

    fun calculateRoute(
        startLocation: Location,
        destinationLocation: Location,
        options: RouteOptions = RouteOptions()
    ): Route? {
        return pathFinder.findPath(
            start = startLocation,
            end = destinationLocation,
            options = options
        )
    }

    fun calculateAccessibleRoute(
        start: Location,
        destination: Location
    ): Route? {
        return pathFinder.findPath(
            start = start,
            end = destination,
            options = RouteOptions(
                accessibleOnly = true,
                avoidStairs = true,
                preferElevators = true
            )
        )
    }

    fun getRouteDistance(route: Route): Float {
        return pathFinder.calculateDistance(route)
    }

    fun getEstimatedTime(route: Route, walkingSpeed: Float = 1.4f): Int {
        val distance = getRouteDistance(route)
        return (distance / walkingSpeed).toInt() // seconds
    }
}
import com.machinestalk.indoornavigationengine.util.PathFindingUtil;
import com.machinestalk.indoornavigationengine.models.Location;
import com.machinestalk.indoornavigationengine.models.Route;
import com.machinestalk.indoornavigationengine.models.RouteOptions;

public class NavigationController {

    private PathFindingUtil pathFinder;

    public NavigationController(MineSceneView sceneView) {
        this.pathFinder = new PathFindingUtil(sceneView);
    }

    public Route calculateRoute(
        Location start,
        Location destination,
        RouteOptions options
    ) {
        return pathFinder.findPath(start, destination, options);
    }

    public Route calculateAccessibleRoute(Location start, Location destination) {
        RouteOptions options = new RouteOptions.Builder()
            .setAccessibleOnly(true)
            .setAvoidStairs(true)
            .setPreferElevators(true)
            .build();

        return pathFinder.findPath(start, destination, options);
    }

    public float getRouteDistance(Route route) {
        return pathFinder.calculateDistance(route);
    }

    public int getEstimatedTime(Route route, float walkingSpeed) {
        float distance = getRouteDistance(route);
        return (int) (distance / walkingSpeed); // seconds
    }
}

Route Options:

Option Type Default Description
accessibleOnly Boolean false Use only wheelchair-accessible paths
avoidStairs Boolean false Prefer ramps and elevators
preferElevators Boolean false Prioritize elevators over escalators
maxDistance Float null Maximum acceptable route distance
avoidAreas List [] Areas to avoid in routing

Path Finding Algorithm

State-of-the-art A* implementation optimized for indoor navigation with enhanced heuristics.

Algorithm Features:

  • ๐Ÿงฎ Optimized A* Search - Efficient node exploration with intelligent heuristics
  • ๐ŸŽฏ Multi-Goal Support - Find paths to multiple destinations simultaneously
  • ๐Ÿ”„ Bidirectional Search - Faster calculation for long-distance routes
  • ๐Ÿ“Š Cost Functions - Customizable weighting for different path preferences
  • ๐ŸŒณ Graph Optimization - Pre-computed navigation meshes for performance
  • ๐Ÿ’พ Route Caching - Smart caching of frequently used routes

Technical Implementation:

import com.machinestalk.indoornavigationengine.pathfinding.PathFindingAlgorithm
import com.machinestalk.indoornavigationengine.pathfinding.AStarConfig

// Configure the A* algorithm
val algorithmConfig = AStarConfig(
    // Heuristic weight (higher = faster but less accurate)
    heuristicWeight = 1.2f,

    // Maximum nodes to explore (prevents infinite loops)
    maxNodesExplored = 10000,

    // Enable diagonal movement
    allowDiagonalMovement = true,

    // Cost for diagonal moves (typically sqrt(2) โ‰ˆ 1.414)
    diagonalCost = 1.414f,

    // Enable path smoothing for more natural routes
    enablePathSmoothing = true,

    // Timeout in milliseconds
    timeout = 5000
)

val algorithm = PathFindingAlgorithm.AStar(algorithmConfig)

// Use custom cost function
algorithm.setCostFunction { from, to ->
    val baseCost = calculateDistance(from, to)

    // Add extra cost for stairs (discourage stair use)
    val stairPenalty = if (to.type == NodeType.STAIRS) 2.0f else 0f

    // Prefer well-lit areas (reduce cost)
    val lightingBonus = if (to.lighting > 0.7f) -0.5f else 0f

    baseCost + stairPenalty + lightingBonus
}

// Find path using the configured algorithm
val path = algorithm.findPath(
    startNode = startLocation.toNode(),
    endNode = destinationLocation.toNode(),
    navigationGraph = sceneView.getNavigationGraph()
)
import com.machinestalk.indoornavigationengine.pathfinding.PathFindingAlgorithm;
import com.machinestalk.indoornavigationengine.pathfinding.AStarConfig;

// Configure the A* algorithm
AStarConfig config = new AStarConfig.Builder()
    .setHeuristicWeight(1.2f)
    .setMaxNodesExplored(10000)
    .setAllowDiagonalMovement(true)
    .setDiagonalCost(1.414f)
    .setEnablePathSmoothing(true)
    .setTimeout(5000)
    .build();

PathFindingAlgorithm algorithm = PathFindingAlgorithm.createAStar(config);

// Use custom cost function
algorithm.setCostFunction((from, to) -> {
    float baseCost = calculateDistance(from, to);

    float stairPenalty = to.getType() == NodeType.STAIRS ? 2.0f : 0f;
    float lightingBonus = to.getLighting() > 0.7f ? -0.5f : 0f;

    return baseCost + stairPenalty + lightingBonus;
});

// Find path
Path path = algorithm.findPath(
    startLocation.toNode(),
    destinationLocation.toNode(),
    sceneView.getNavigationGraph()
);

Performance Benchmarks:

Scenario Distance Nodes Time Memory
Same floor, short 20m 45 8ms 2KB
Same floor, long 150m 320 35ms 12KB
Multi-floor 200m 580 65ms 25KB
Complex maze 100m 1200 120ms 45KB

Path Finding UI Components

Complete set of pre-built UI components for displaying navigation instructions and route information.

Available Components:

import com.machinestalk.indoornavigationengine.ui.NavigationInstructionPanel

val instructionPanel = NavigationInstructionPanel(this).apply {
    // Configure appearance
    setPosition(NavigationInstructionPanel.Position.TOP_CENTER)
    setBackgroundColor(Color.parseColor("#2196F3"))
    setTextColor(Color.WHITE)

    // Set instruction
    showInstruction(
        action = NavigationAction.TURN_LEFT,
        distance = 25f,
        description = "Turn left at the food court"
    )

    // Show next instruction preview
    showNextInstruction(
        action = NavigationAction.GO_STRAIGHT,
        distance = 50f
    )

    // Handle instruction tap
    setOnInstructionClickListener {
        // Show detailed instructions
        showDetailedDirections()
    }
}

sceneView.addUIComponent(instructionPanel)

Route Progress Bar

import com.machinestalk.indoornavigationengine.ui.RouteProgressBar

val progressBar = RouteProgressBar(this).apply {
    setPosition(RouteProgressBar.Position.BOTTOM)

    // Set route information
    setTotalDistance(250f) // meters
    setRemainingDistance(180f)
    setEstimatedTime(180) // seconds

    // Customize appearance
    setProgressColor(Color.parseColor("#4CAF50"))
    setBackgroundColor(Color.parseColor("#E0E0E0"))
    setHeight(8.dp)

    // Enable auto-update
    enableAutoUpdate = true
}

sceneView.addUIComponent(progressBar)

Route Preview Card

import com.machinestalk.indoornavigationengine.ui.RoutePreviewCard

val routeCard = RoutePreviewCard(this).apply {
    // Set route details
    setDestination("Coffee Shop - Floor 2")
    setDistance(185f)
    setEstimatedTime(135) // seconds
    setFloorChanges(1)

    // Show route alternatives
    addAlternativeRoute(
        name = "Shorter route",
        distance = 165f,
        time = 120,
        note = "Via stairs"
    )

    addAlternativeRoute(
        name = "Accessible route",
        distance = 210f,
        time = 155,
        note = "Elevator access"
    )

    // Handle route selection
    setOnRouteSelectedListener { routeIndex ->
        startNavigation(routes[routeIndex])
    }
}

sceneView.addUIComponent(routeCard)

Turn-by-Turn Direction List

import com.machinestalk.indoornavigationengine.ui.DirectionListView

val directionList = DirectionListView(this).apply {
    // Add directions from route
    route.instructions.forEach { instruction ->
        addDirection(
            icon = getInstructionIcon(instruction.action),
            text = instruction.description,
            distance = instruction.distance
        )
    }

    // Highlight current instruction
    setCurrentStep(0)

    // Handle step selection
    setOnStepSelectedListener { stepIndex ->
        // Preview this step on map
        previewStep(route.instructions[stepIndex])
    }
}

// Show in bottom sheet
showBottomSheet(directionList)

UI Component Customization:

// Global UI configuration for all navigation components
NavigationUIConfig.apply {
    // Colors
    primaryColor = Color.parseColor("#2196F3")
    accentColor = Color.parseColor("#FF5722")
    textColor = Color.parseColor("#212121")

    // Typography
    fontFamily = ResourcesCompat.getFont(context, R.font.custom_font)
    titleTextSize = 18.sp
    bodyTextSize = 14.sp

    // Spacing
    defaultPadding = 16.dp
    componentMargin = 8.dp

    // Animations
    animationDuration = 300
    enableTransitions = true
}

Custom Path Finding Algorithms

Extend the navigation engine with your own routing algorithms for specialized use cases.

Plugin Architecture:

import com.machinestalk.indoornavigationengine.pathfinding.CustomPathFinder
import com.machinestalk.indoornavigationengine.pathfinding.PathFinderPlugin

// Create custom algorithm
class ShortestTimePathFinder : PathFinderPlugin {

    override val name = "Shortest Time Algorithm"
    override val version = "1.0.0"

    override fun findPath(
        start: Node,
        end: Node,
        graph: NavigationGraph,
        options: RouteOptions
    ): Path? {
        // Implement custom logic
        // This example prioritizes faster routes over shorter distances

        val openSet = PriorityQueue<Node>(compareBy { it.estimatedTime })
        val closedSet = mutableSetOf<Node>()

        openSet.add(start)

        while (openSet.isNotEmpty()) {
            val current = openSet.poll()

            if (current == end) {
                return reconstructPath(current)
            }

            closedSet.add(current)

            graph.getNeighbors(current).forEach { neighbor ->
                if (neighbor in closedSet) return@forEach

                val travelTime = calculateTravelTime(current, neighbor)
                val newTime = current.totalTime + travelTime

                if (newTime < neighbor.totalTime) {
                    neighbor.totalTime = newTime
                    neighbor.parent = current

                    if (neighbor !in openSet) {
                        openSet.add(neighbor)
                    }
                }
            }
        }

        return null // No path found
    }

    private fun calculateTravelTime(from: Node, to: Node): Float {
        val distance = from.distanceTo(to)

        // Different speeds for different path types
        val speed = when (to.type) {
            NodeType.WALKWAY -> 1.4f // m/s
            NodeType.STAIRS -> 0.6f
            NodeType.ESCALATOR -> 0.8f
            NodeType.ELEVATOR -> 0.5f // includes wait time
            else -> 1.0f
        }

        return distance / speed
    }
}

// Register custom algorithm
val customAlgorithm = ShortestTimePathFinder()
PathFindingUtil.registerAlgorithm(customAlgorithm)

// Use custom algorithm
val route = pathFinder.findPath(
    start = startLocation,
    end = destinationLocation,
    algorithm = customAlgorithm
)
import com.machinestalk.indoornavigationengine.pathfinding.PathFinderPlugin;

public class ShortestTimePathFinder implements PathFinderPlugin {

    @Override
    public String getName() {
        return "Shortest Time Algorithm";
    }

    @Override
    public String getVersion() {
        return "1.0.0";
    }

    @Override
    public Path findPath(
        Node start,
        Node end,
        NavigationGraph graph,
        RouteOptions options
    ) {
        // Custom implementation
        PriorityQueue<Node> openSet = new PriorityQueue<>(
            Comparator.comparing(Node::getEstimatedTime)
        );
        Set<Node> closedSet = new HashSet<>();

        openSet.add(start);

        while (!openSet.isEmpty()) {
            Node current = openSet.poll();

            if (current.equals(end)) {
                return reconstructPath(current);
            }

            closedSet.add(current);

            for (Node neighbor : graph.getNeighbors(current)) {
                if (closedSet.contains(neighbor)) continue;

                float travelTime = calculateTravelTime(current, neighbor);
                float newTime = current.getTotalTime() + travelTime;

                if (newTime < neighbor.getTotalTime()) {
                    neighbor.setTotalTime(newTime);
                    neighbor.setParent(current);

                    if (!openSet.contains(neighbor)) {
                        openSet.add(neighbor);
                    }
                }
            }
        }

        return null;
    }
}

Algorithm Selection:

// List available algorithms
val algorithms = PathFindingUtil.getAvailableAlgorithms()
algorithms.forEach { algorithm ->
    println("${algorithm.name} v${algorithm.version}")
}

// Use specific algorithm
val route = pathFinder.findPath(
    start = startLocation,
    end = destinationLocation,
    algorithmName = "Shortest Time Algorithm"
)

Performance Optimizations

Significant improvements in path calculation speed and resource utilization.

Optimization Techniques:

  1. Navigation Graph Preprocessing

    // Pre-compute navigation graph for faster queries
    sceneView.preprocessNavigationGraph(
        granularity = GraphGranularity.HIGH,
        includeAccessibilityInfo = true
    )
    

  2. Route Caching

    // Enable intelligent route caching
    PathFindingUtil.enableCaching(
        maxCacheSize = 100,
        ttl = 3600 // seconds
    )
    

  3. Incremental Updates

    // Update only affected portions of the graph
    navigationGraph.updateRegion(
        bounds = changedArea,
        incremental = true
    )
    

Performance Improvements:

Metric v0.2.1 v0.3.2 Improvement
Route Calculation 180ms 65ms โฌ‡๏ธ 64%
Memory Usage 45MB 28MB โฌ‡๏ธ 38%
Graph Loading 2.1s 0.8s โฌ‡๏ธ 62%
Cache Hit Rate N/A 78% โฌ†๏ธ New
CPU Usage 35% 18% โฌ‡๏ธ 49%

Bug Fixes

Path Finding Algorithm Stability

Issue: The initial path finding implementation had reliability issues: - Occasional crashes when calculating routes on complex maps - Infinite loops in certain edge cases with circular layouts - Memory leaks during long-running navigation sessions - Incorrect routes when dealing with disconnected graph regions

Root Cause: - Insufficient validation of graph connectivity - Missing termination conditions in path search - Improper cleanup of temporary path finding data structures - Edge cases in multi-floor transition handling

Resolution:

// Added comprehensive graph validation
fun validateNavigationGraph(graph: NavigationGraph): ValidationResult {
    val issues = mutableListOf<String>()

    // Check connectivity
    if (!graph.isFullyConnected()) {
        issues.add("Graph contains disconnected regions")
    }

    // Validate nodes
    graph.nodes.forEach { node ->
        if (node.neighbors.isEmpty() && node.type != NodeType.DESTINATION) {
            issues.add("Isolated node detected: ${node.id}")
        }
    }

    // Check for invalid edges
    graph.edges.forEach { edge ->
        if (edge.cost < 0) {
            issues.add("Negative edge cost: ${edge.id}")
        }
    }

    return if (issues.isEmpty()) {
        ValidationResult.Valid
    } else {
        ValidationResult.Invalid(issues)
    }
}

// Added safety limits
val pathResult = pathFinder.findPath(
    start = start,
    end = end,
    config = AStarConfig(
        maxNodesExplored = 10000, // Prevents infinite loops
        timeout = 5000, // 5 second timeout
        enableSafetyChecks = true
    )
)

Impact: - 99.8% success rate for path calculations (up from 94%) - Zero crashes related to path finding in testing - Memory usage reduced by 38%


Path Finding UI Display Issues

Issue: Navigation UI components had several display problems: - Instruction panel text occasionally cut off on small screens - Route progress bar not updating in real-time - Direction icons not loading correctly - Flickering during route updates

Resolution: - Implemented responsive text sizing based on available space - Added real-time location tracking integration for progress updates - Pre-loaded and cached navigation icons - Optimized rendering pipeline to eliminate flickering

Before:

// UI updates caused visual glitches
instructionPanel.updateInstruction(newInstruction)
progressBar.setProgress(newProgress)
// Screen would flicker during updates

After:

// Batch updates for smooth transitions
sceneView.batchUIUpdate {
    instructionPanel.updateInstruction(newInstruction)
    progressBar.setProgress(newProgress)
    // Updates applied atomically with animations
}

Technical Improvements: - Reduced UI update latency by 75% - Eliminated flickering through double-buffering - Improved text rendering with dynamic sizing - Added smooth animation transitions


JSON Parsing for Path Finding Configuration

Issue: Parser failed when path finding configuration JSON was empty or malformed, causing: - Application crashes during map initialization - Silent failures without error messages - Incorrect default values being used - Null pointer exceptions

Resolution:

// Robust JSON parsing with fallbacks
fun loadPathFindingConfig(fileName: String): PathFindingConfig {
    return try {
        val json = JsonUtil.LoadJsonFromAsset(context, fileName)

        if (json.isNullOrEmpty()) {
            Log.w("PathFinding", "Empty config file, using defaults")
            return PathFindingConfig.createDefault()
        }

        val config = parsePathFindingConfig(json)

        // Validate parsed config
        if (!config.isValid()) {
            Log.w("PathFinding", "Invalid config, using defaults")
            return PathFindingConfig.createDefault()
        }

        config

    } catch (e: JsonSyntaxException) {
        Log.e("PathFinding", "JSON syntax error: ${e.message}")
        PathFindingConfig.createDefault()
    } catch (e: Exception) {
        Log.e("PathFinding", "Failed to load config: ${e.message}")
        PathFindingConfig.createDefault()
    }
}

// Default configuration
fun PathFindingConfig.Companion.createDefault() = PathFindingConfig(
    algorithm = "A*",
    heuristicWeight = 1.0f,
    allowDiagonalMovement = true,
    enableCaching = true,
    maxNodesExplored = 10000
)

Additional Safety Features: - Schema validation for JSON structure - Detailed error logging with line numbers - Graceful fallback to defaults - Configuration validation before use


Known Issues

Zero Known Issues

We're proud to announce that version 0.3.2 has no known critical or major issues!

All reported bugs from previous versions have been resolved, and extensive testing across multiple device configurations has not revealed any significant problems.

Testing Coverage:

  • โœ… Path finding on 50+ different map configurations
  • โœ… Navigation across 20+ device models
  • โœ… Stress testing with complex multi-floor routes
  • โœ… Memory leak testing with 8+ hour sessions
  • โœ… Performance testing on low-end devices
  • โœ… UI testing across all screen sizes

If you encounter any issues, please report them immediately:

Report an Issue


Upgrade from v0.2.1

Breaking Changes

Minor Breaking Changes

This release includes a few minor API changes that may require code updates.

PathFinder Constructor

Before (v0.2.1):

val pathFinder = PathFinder(context)

After (v0.3.2):

val pathFinder = PathFindingUtil(sceneView)
// Now requires MineSceneView instead of Context

Route Options

Before:

val route = pathFinder.findPath(start, end, accessible = true)

After:

val options = RouteOptions(accessibleOnly = true)
val route = pathFinder.findPath(start, end, options)

Migration Steps

  1. Update Dependency

=== "Gradle (Kotlin)"

   ```kotlin
   dependencies {
       implementation("com.machinestalk:indoornavigationengine:0.3.2-alpha")
   }
   ```

=== "Gradle (Groovy)"

   ```groovy
   dependencies {
       implementation 'com.machinestalk:indoornavigationengine:0.3.2-alpha'
   }
   ```
  1. Sync and Clean Build

    ./gradlew clean build
    

  2. Update PathFinder Initialization

    // Old
    val pathFinder = PathFinder(context)
    
    // New
    val pathFinder = PathFindingUtil(sceneView)
    

  3. Update Route Options

    // Old
    val route = pathFinder.findPath(start, end, true)
    
    // New
    val options = RouteOptions(accessibleOnly = true)
    val route = pathFinder.findPath(start, end, options)
    

  4. Add Navigation UI Components (Optional)

    sceneView.addUIComponent(NavigationInstructionPanel(context))
    sceneView.addUIComponent(RouteProgressBar(context))
    

New Feature Integration

Take advantage of new features:

// Enable path finding optimizations
PathFindingUtil.enableCaching(maxCacheSize = 100)
sceneView.preprocessNavigationGraph()

// Use new UI components
val instructionPanel = NavigationInstructionPanel(this)
val progressBar = RouteProgressBar(this)

sceneView.apply {
    addUIComponent(instructionPanel)
    addUIComponent(progressBar)
}

// Implement real-time navigation
pathFinder.startNavigation(route) { instruction ->
    instructionPanel.showInstruction(instruction)
}

Testing & Quality

Test Coverage

Component Unit Tests Integration Coverage
Path Finding Core โœ… 156 tests โœ… 45 tests 94%
A* Algorithm โœ… 78 tests โœ… 22 tests 96%
UI Components โœ… 89 tests โœ… 34 tests 88%
Route Calculation โœ… 112 tests โœ… 38 tests 92%
Graph Processing โœ… 67 tests โœ… 18 tests 90%

Device Testing Matrix

Device Category Models Tested Pass Rate Notes
Flagship Phones 15 models 100% โœ… Excellent performance
Mid-range Phones 22 models 100% โœ… Good performance
Budget Phones 18 models 98% โœ… Minor lag on 2 models
Tablets (7"-8") 8 models 100% โœ… Optimal experience
Tablets (10"+) 12 models 100% โœ… Outstanding UX

Performance Benchmarks

Path Finding Performance:

Map Complexity Avg. Time Max Time Success Rate
Simple (< 100 nodes) 12ms 25ms 100%
Medium (100-500 nodes) 45ms 95ms 100%
Complex (500-1000 nodes) 120ms 280ms 99.8%
Very Complex (> 1000 nodes) 350ms 720ms 99.5%

Memory Footprint:

  • Idle: 28MB (โฌ‡๏ธ 38% from v0.2.1)
  • Active Navigation: 42MB (โฌ‡๏ธ 32% from v0.2.1)
  • Peak Usage: 68MB (โฌ‡๏ธ 25% from v0.2.1)

What's Next - v0.4.0

We're already working on the next exciting features:

Confirmed Features

  • ๐Ÿ—บ๏ธ Offline Map Support - Download maps for offline navigation
  • ๐Ÿ”” Proximity Alerts - Notifications when approaching destinations
  • ๐Ÿ“Š Analytics Dashboard - Usage statistics and insights
  • ๐ŸŽค Voice Guidance - Audio turn-by-turn directions
  • ๐ŸŒ Multi-Language Support - Internationalization
  • โ™ฟ Enhanced Accessibility - More accessibility options

Under Consideration

  • ๐ŸŽฎ AR Navigation Mode - Augmented reality wayfinding
  • ๐Ÿค Crowd Routing - Avoid crowded areas
  • ๐Ÿ…ฟ๏ธ Parking Integration - Find and navigate to parking
  • ๐Ÿข Building Directory - Interactive tenant/store listings
  • ๐Ÿ“ฑ Widget Support - Quick navigation widgets

Timeline

Expected Release: Q4 2024
Beta Program: Starting October 2024

Get Notified


Complete Navigation Example

Here's a comprehensive example implementing all path finding features:

package com.example.indoornavapp

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.machinestalk.indoornavigationengine.ui.MineSceneView
import com.machinestalk.indoornavigationengine.ui.*
import com.machinestalk.indoornavigationengine.util.*
import com.machinestalk.indoornavigationengine.models.*

class NavigationActivity : AppCompatActivity() {

    private lateinit var sceneView: MineSceneView
    private lateinit var pathFinder: PathFindingUtil
    private lateinit var instructionPanel: NavigationInstructionPanel
    private lateinit var progressBar: RouteProgressBar

    private var currentRoute: Route? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Setup scene
        sceneView = MineSceneView(this)
        setContentView(sceneView)

        // Load map
        val mapData = JsonUtil.LoadJsonFromAsset(this, "maps/mall.json")
        mapData?.let { sceneView.setMapData(it) }

        // Initialize path finding
        setupPathFinding()

        // Setup UI components
        setupNavigationUI()
    }

    private fun setupPathFinding() {
        pathFinder = PathFindingUtil(sceneView)

        // Enable optimizations
        PathFindingUtil.enableCaching(maxCacheSize = 100)
        sceneView.preprocessNavigationGraph(
            granularity = GraphGranularity.HIGH
        )
    }

    private fun setupNavigationUI() {
        // Instruction panel
        instructionPanel = NavigationInstructionPanel(this).apply {
            setPosition(NavigationInstructionPanel.Position.TOP_CENTER)
        }
        sceneView.addUIComponent(instructionPanel)

        // Progress bar
        progressBar = RouteProgressBar(this).apply {
            setPosition(RouteProgressBar.Position.BOTTOM)
            enableAutoUpdate = true
        }
        sceneView.addUIComponent(progressBar)
    }

    fun navigateToDestination(destinationId: String) {
        val currentLocation = getCurrentLocation()
        val destination = sceneView.getPOIById(destinationId)

        if (destination != null) {
            calculateAndStartNavigation(currentLocation, destination.location)
        }
    }

    private fun calculateAndStartNavigation(
        start: Location,
        destination: Location
    ) {
        // Show loading
        showLoading()

        // Calculate route
        val options = RouteOptions(
            accessibleOnly = false,
            preferElevators = true
        )

        val route = pathFinder.findPath(start, destination, options)

        route?.let {
            currentRoute = it

            // Display route on map
            sceneView.drawRoute(
                route = it,
                color = getColor(R.color.route_color),
                width = 8f,
                animated = true
            )

            // Setup navigation UI
            val distance = pathFinder.calculateDistance(it)
            val time = (distance / 1.4f).toInt()

            progressBar.apply {
                setTotalDistance(distance)
                setEstimatedTime(time)
            }

            // Start turn-by-turn navigation
            pathFinder.startNavigation(it) { instruction ->
                instructionPanel.showInstruction(
                    action = instruction.action,
                    distance = instruction.distance,
                    description = instruction.description
                )
            }

            hideLoading()
        } ?: run {
            hideLoading()
            showError("Unable to calculate route")
        }
    }

    override fun onDestroy() {
        pathFinder.stopNavigation()
        super.onDestroy()
    }
}

Feedback & Support

We value your feedback!


Additional Resources


Acknowledgments

Special thanks to our beta testers and early adopters who provided invaluable feedback on the path finding system!


License

This software is released under the commercial license. Please review the license terms before use.


๐ŸŽ‰ Welcome to the Future of Indoor Navigation!

Version 0.3.2 brings powerful path finding capabilities to your fingertips. Start building amazing navigation experiences today!