# Parking Spot: Mark & Navigate Back

## Introduction

A common “last 200 meters” problem in large campuses, hospitals, airports, shopping centers, and multi-level parking garages is simply remembering where you parked and later finding your way back to that same location

This use case guide shows one recommended approach for implementing a “Mark my parking spot” feature in a MapsIndoors-powered application:

1. The user marks a parking spot on the map (using their live position or a dropped pin)
2. The app saves that spot (typically per-user)
3. Later, the user can request directions back to that exact spot using the same wayfinding experience already in your MapsIndoors app

## ParkingManager

A reusable ES Module for parking spot management that integrates with MapsIndoors SDK.

### Table of Contents

* Overview
* Summary
* Installation
* Quick Start
* API Reference
* Storage Adapters
* MapsIndoors Integration
* Examples

***

### Overview

ParkingManager provides a simple API to save, load, and manage parking spot locations. It's designed to work seamlessly with MapsIndoors SDK location objects and supports configurable storage backends.

#### MapsIndoors Features

* [Way finding](/sdks-and-frameworks/web/directions-and-routing.md)

#### Additional Features

* Save MapsIndoors locations as parking spots
* Persistent storage via localStorage or cookies
* Custom storage adapter support

#### Saved Data Structure

When a parking spot is saved, ParkingManager stores the following data:

```ts
{
  id: string,           // MapsIndoors location ID
  name: string,         // Location name
  description: string,  // Location description
  coordinates: {
    lat: number,        // Latitude
    lng: number         // Longitude
  },
  floorIndex: number,   // Floor level
  savedAt: number       // Unix timestamp (milliseconds)
}
```

***

### Summary

A “Mark & Navigate Back” parking workflow is a compact but high-impact MapsIndoors use case:

* Save a point (usually per user)
* Render it clearly
* Route back using MapsIndoors wayfinding components

Because the pattern is generic (save a coordinate + optional floor + navigate later), you can reuse the same architecture for many other use cases like “Find my desk”, “Return to entrance”, “Meet me here”, or “Remember this place”.

***

### Browser Support

ParkingManager requires ES6+ support and works in all modern browsers:

* Chrome 61+
* Firefox 60+
* Safari 11+
* Edge 16+

For localStorage adapter, ensure localStorage is available. For cookie adapter, ensure cookies are enabled.

***

### Installation

Include the module in your HTML file with ES module support:

```html
<script type="module" src="script.js"></script>
```

Import in your JavaScript:

```javascript
import { ParkingManager, LocalStorageAdapter, CookieStorageAdapter } from './parking.js';
```

***

### Quick Start

```javascript
import { ParkingManager } from './parking.js';

// Create a ParkingManager instance (uses localStorage by default)
const parking = new ParkingManager();

// Save a MapsIndoors location as parking
parking.saveParkingSpot(mapsIndoorsLocation);

// Check if parking exists
if (parking.hasSavedParking()) {
  // Load the saved parking data
  const savedParking = parking.loadParkingSpot();
  console.log('Parked at:', savedParking.name);
}

// Clear saved parking
parking.clearParkingSpot();
```

***

### API Reference

#### ParkingManager

**Constructor**

```javascript
new ParkingManager(options?)
```

| Option           | Type   | Default               | Description            |
| ---------------- | ------ | --------------------- | ---------------------- |
| `storageAdapter` | Object | `LocalStorageAdapter` | Storage backend to use |
| `storageKey`     | string | `'savedParkingSpot'`  | Key used for storage   |

**Methods**

**`saveParkingSpot(mapsIndoorsLocation)`**

Saves a MapsIndoors location as the parking spot.

```javascript
parking.saveParkingSpot(location);
```

| Parameter             | Type   | Description                 |
| --------------------- | ------ | --------------------------- |
| `mapsIndoorsLocation` | Object | MapsIndoors location object |

**Returns:** `boolean` - `true` if save was successful

**`loadParkingSpot()`**

Loads the saved parking spot data.

```javascript
const savedParking = parking.loadParkingSpot();
```

**Returns:** `Object | null` - Saved parking data or `null` if none exists

**`clearParkingSpot()`**

Clears the saved parking spot.

```javascript
parking.clearParkingSpot();
```

**Returns:** `boolean` - `true` if clear was successful

**`hasSavedParking()`**

Checks if a parking spot is saved.

```javascript
if (parking.hasSavedParking()) {
  // Parking spot exists
}
```

**Returns:** `boolean` - `true` if a parking spot exists

***

### Storage Adapters

#### LocalStorageAdapter (Default)

Uses browser localStorage for persistent storage.

```javascript
import { ParkingManager, LocalStorageAdapter } from './parking.js';

const parking = new ParkingManager({
  storageAdapter: new LocalStorageAdapter({
    prefix: 'myApp_'  // Optional key prefix
  })
});
```

| Option   | Type   | Default | Description             |
| -------- | ------ | ------- | ----------------------- |
| `prefix` | string | `''`    | Prefix for storage keys |

#### CookieStorageAdapter

Uses browser cookies with configurable expiration.

```javascript
import { ParkingManager, CookieStorageAdapter } from './parking.js';

const parking = new ParkingManager({
  storageAdapter: new CookieStorageAdapter({
    expireDays: 7,      // Cookie expiration in days
    path: '/',          // Cookie path
    secure: false,      // Secure flag (HTTPS only)
    sameSite: 'Lax'     // SameSite policy
  })
});
```

| Option       | Type    | Default | Description               |
| ------------ | ------- | ------- | ------------------------- |
| `expireDays` | number  | `7`     | Days until cookie expires |
| `path`       | string  | `'/'`   | Cookie path               |
| `secure`     | boolean | `false` | Require HTTPS             |
| `sameSite`   | string  | `'Lax'` | SameSite attribute        |

#### Custom Storage Adapter

Create your own adapter by implementing the storage interface:

```javascript
class MyCustomAdapter {
  get(key) {
    // Return parsed data or null
  }

  set(key, value) {
    // Store data, return true on success
  }

  remove(key) {
    // Remove data, return true on success
  }
}

const parking = new ParkingManager({
  storageAdapter: new MyCustomAdapter()
});
```

**Example: SessionStorage Adapter**

```javascript
class SessionStorageAdapter {
  get(key) {
    try {
      const value = sessionStorage.getItem(key);
      return value ? JSON.parse(value) : null;
    } catch (e) {
      return null;
    }
  }

  set(key, value) {
    try {
      sessionStorage.setItem(key, JSON.stringify(value));
      return true;
    } catch (e) {
      return false;
    }
  }

  remove(key) {
    try {
      sessionStorage.removeItem(key);
      return true;
    } catch (e) {
      return false;
    }
  }
}
```

***

### MapsIndoors Integration

#### Complete Setup Example

```javascript
import { ParkingManager, CookieStorageAdapter } from './parking.js';

// Initialize ParkingManager
const parkingManager = new ParkingManager({
  storageAdapter: new CookieStorageAdapter({ expireDays: 7 }),
  storageKey: 'venue_parking_spot'
});

// Initialize MapsIndoors
const mapViewOptions = {
  accessToken: 'YOUR_MAPBOX_TOKEN',
  element: document.getElementById('map'),
  center: { lat: 0, lng: 0 },
  zoom: 17
};

const mapView = new mapsindoors.mapView.MapboxV3View(mapViewOptions);
const mapsIndoors = new mapsindoors.MapsIndoors({
  mapView: mapView,
  venue: 'YOUR_VENUE_ID'
});

// Load saved parking on startup
let savedParkingSpot = parkingManager.loadParkingSpot();

// Center map on saved parking when ready
mapsIndoors.addListener('ready', () => {
  if (savedParkingSpot) {
    const map = mapView.getMap();
    map.setCenter({
      lat: savedParkingSpot.coordinates.lat,
      lng: savedParkingSpot.coordinates.lng
    });
    mapsIndoors.setFloor(savedParkingSpot.floorIndex);
  }
});
```

#### Saving Parking from Location Click

```javascript
// Handle location clicks
mapsIndoors.on('click', (location) => {
  if (location && isParkingLocation(location)) {
    showSaveParkingButton(location);
  }
});

// Check if location is a parking spot
function isParkingLocation(location) {
  const type = (location.properties.type || '').toLowerCase();
  const name = (location.properties.name || '').toLowerCase();
  return type.includes('parking') || name.includes('parking');
}

// Save parking button handler
function saveParkingHandler(location) {
  const success = parkingManager.saveParkingSpot(location);

  if (success) {
    savedParkingSpot = parkingManager.loadParkingSpot();
    showSuccessMessage('Parking spot saved!');
    updateParkingUI();
  } else {
    showErrorMessage('Failed to save parking spot');
  }
}
```

#### Navigating to Saved Parking

```javascript
function navigateToParking() {
  const parking = parkingManager.loadParkingSpot();

  if (!parking) {
    showErrorMessage('No parking spot saved');
    return;
  }

  // Center map on parking location
  const map = mapView.getMap();
  map.setCenter({
    lat: parking.coordinates.lat,
    lng: parking.coordinates.lng
  });
  mapsIndoors.setFloor(parking.floorIndex);

  // Create destination for directions
  const destination = {
    lat: parking.coordinates.lat,
    lng: parking.coordinates.lng,
    floor: parking.floorIndex
  };

  // Calculate route from current position
  calculateDirections(currentPosition, destination);
}
```

#### Getting Directions to Parking

```javascript
async function getDirectionsToParking(origin) {
  const parking = parkingManager.loadParkingSpot();

  if (!parking) {
    console.error('No parking spot saved');
    return;
  }

  // Setup directions service
  const externalProvider = new mapsindoors.directions.MapboxProvider('YOUR_MAPBOX_TOKEN');
  const directionsService = new mapsindoors.services.DirectionsService(externalProvider);
  const directionsRenderer = new mapsindoors.directions.DirectionsRenderer({
    mapsIndoors: mapsIndoors
  });

  // Calculate route
  const route = await directionsService.getRoute({
    origin: {
      lat: origin.lat,
      lng: origin.lng,
      floor: origin.floor
    },
    destination: {
      lat: parking.coordinates.lat,
      lng: parking.coordinates.lng,
      floor: parking.floorIndex
    },
    travelMode: 'WALKING'
  });

  // Display route on map
  directionsRenderer.setRoute(route);
}
```

***

### Examples

<details>

<summary>Example 1: Basic Usage with LocalStorage</summary>

```javascript
import { ParkingManager } from './parking.js';

const parking = new ParkingManager();

// Save when user clicks a parking location
document.getElementById('save-btn').addEventListener('click', () => {
  if (currentLocation) {
    parking.saveParkingSpot(currentLocation);
    alert('Parking saved!');
  }
});

// Load on page load
window.addEventListener('load', () => {
  if (parking.hasSavedParking()) {
    const saved = parking.loadParkingSpot();
    document.getElementById('parking-name').textContent = saved.name;
  }
});
```

</details>

<details>

<summary>Example 2: Cookie Storage with Custom Key</summary>

```javascript
import { ParkingManager, CookieStorageAdapter } from './parking.js';

const parking = new ParkingManager({
  storageAdapter: new CookieStorageAdapter({
    expireDays: 30,
    secure: true,
    sameSite: 'Strict'
  }),
  storageKey: 'stadium_parking_v1'
});
```

</details>

<details>

<summary>Example 3: Display Saved Parking Info</summary>

```javascript
function updateParkingDisplay() {
  const parking = parkingManager.loadParkingSpot();
  const container = document.getElementById('parking-info');

  if (parking) {
    // Format time since saved
    const savedDate = new Date(parking.savedAt);
    const minutesAgo = Math.floor((Date.now() - parking.savedAt) / 60000);

    let timeText;
    if (minutesAgo < 1) {
      timeText = 'Just now';
    } else if (minutesAgo < 60) {
      timeText = `${minutesAgo} minute${minutesAgo > 1 ? 's' : ''} ago`;
    } else {
      const hoursAgo = Math.floor(minutesAgo / 60);
      timeText = `${hoursAgo} hour${hoursAgo > 1 ? 's' : ''} ago`;
    }

    container.innerHTML = `
      <h3>${parking.name}</h3>
      <p>Floor: ${parking.floorIndex}</p>
      <p>Saved: ${timeText}</p>
      <button onclick="navigateToParking()">Navigate</button>
      <button onclick="clearParking()">Clear</button>
    `;
  } else {
    container.innerHTML = '<p>No parking spot saved</p>';
  }
}
```

</details>

<details>

<summary>Example 4: Search and Save Parking</summary>

```javascript
async function searchAndSaveParking(query) {
  // Search for parking locations
  const locations = await mapsindoors.services.LocationsService.getLocations({
    q: query,
    types: ['parking'],
    take: 10
  });

  if (locations.length > 0) {
    // Save the first result
    const parkingLocation = locations[0];
    parkingManager.saveParkingSpot(parkingLocation);

    console.log('Saved parking:', parkingLocation.properties.name);
    return true;
  }

  return false;
}

// Usage
searchAndSaveParking('North Parking Lot');
```

</details>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.mapsindoors.com/academy/use-case-guides/parking-spot-mark-and-navigate-back.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
