# Location Operations

Location endpoints provide comprehensive location search and geocoding services for car rental operations. These
endpoints enable users to find pickup and drop-off locations with autocomplete functionality and detailed location
information.

## Location Search

Search for car rental locations using text queries. This endpoint provides autocomplete functionality, returning
location predictions that match the search query for an enhanced user experience.

### Endpoint


```
GET /locations?query={query}
```

### Authentication

This endpoint requires authentication. Include your JWT access token in the Authorization header:


```
Authorization: Bearer YOUR_ACCESS_TOKEN
```

### Query Parameters

| Parameter | Type | Required | Description |
|  --- | --- | --- | --- |
| `query` | string | Yes | Search query text (minimum 2 characters) |


### Example Request


```bash
curl -X GET "https://api.pro.yolcu360.com/api/v1/locations?query=istanbul" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Response

**Success Response (200 OK):**


```json
[
  {
    "placeId": "ChIJOwg_06VPwokRYv534QaPC8g",
    "description": "Istanbul, Turkey",
    "mainText": "Istanbul",
    "secondaryText": "Turkey",
    "types": [
      "locality",
      "political"
    ]
  },
  {
    "placeId": "ChIJB3uraJq9yhQRvHRAVQ6KFT4",
    "description": "Istanbul Airport (IST), Tayakadın, Arnavutköy/İstanbul, Turkey",
    "mainText": "Istanbul Airport (IST)",
    "secondaryText": "Tayakadın, Arnavutköy/İstanbul, Turkey",
    "types": [
      "airport",
      "establishment",
      "point_of_interest"
    ]
  },
  {
    "placeId": "ChIJ8dYSQGq8yhQRy_TlKn0Qmgg",
    "description": "Sabiha Gökçen International Airport (SAW), Pendik/İstanbul, Turkey",
    "mainText": "Sabiha Gökçen International Airport (SAW)",
    "secondaryText": "Pendik/İstanbul, Turkey",
    "types": [
      "airport",
      "establishment",
      "point_of_interest"
    ]
  },
  {
    "placeId": "ChIJh8tEqiq9yhQR4Es7hO_KWw8",
    "description": "Istanbul Atatürk Airport (ISL), Yeşilköy, Bakırköy/İstanbul, Turkey",
    "mainText": "Istanbul Atatürk Airport (ISL)",
    "secondaryText": "Yeşilköy, Bakırköy/İstanbul, Turkey",
    "types": [
      "airport",
      "establishment",
      "point_of_interest"
    ]
  },
  {
    "placeId": "ChIJu46S-ZNjyhQROG9z8KiBUUs",
    "description": "Sultanahmet, Fatih/İstanbul, Turkey",
    "mainText": "Sultanahmet",
    "secondaryText": "Fatih/İstanbul, Turkey",
    "types": [
      "neighborhood",
      "political"
    ]
  }
]
```

### Response Fields

| Field | Type | Description |
|  --- | --- | --- |
| `placeId` | string | Unique identifier for the location |
| `description` | string | Full description of the location |
| `mainText` | string | Primary location text (typically the name) |
| `secondaryText` | string | Secondary descriptive text (typically the address) |
| `types` | array | Location types (e.g., airport, city, neighborhood) |


### Location Types

| Type | Description |
|  --- | --- |
| `airport` | Airport locations |
| `locality` | Cities or towns |
| `neighborhood` | Districts or neighborhoods |
| `establishment` | Business establishments |
| `point_of_interest` | Notable landmarks or points of interest |
| `political` | Administrative boundaries |


## Get Location Details

Retrieve detailed information about a specific location using its place ID. This endpoint provides comprehensive
location data including coordinates, timezone, and administrative information.

### Endpoint


```
GET /locations/{placeId}
```

### Authentication

This endpoint requires authentication. Include your JWT access token in the Authorization header.

### Path Parameters

| Parameter | Type | Required | Description |
|  --- | --- | --- | --- |
| `placeId` | string | Yes | Unique place identifier from location search |


### Example Request


```bash
curl -X GET https://api.pro.yolcu360.com/api/v1/locations/ChIJOwg_06VPwokRYv534QaPC8g \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

### Response

**Success Response (200 OK):**


```json
{
  "placeId": "ChIJOwg_06VPwokRYv534QaPC8g",
  "name": "Istanbul",
  "city": "Istanbul",
  "countryCode": "TR",
  "timezone": "Europe/Istanbul",
  "point": {
    "lat": 41.0082,
    "lon": 28.9784
  }
}
```

### Response Fields

| Field | Type | Description |
|  --- | --- | --- |
| `placeId` | string | Unique place identifier |
| `name` | string | Location name |
| `city` | string | City name |
| `countryCode` | string | ISO country code (2 characters) |
| `timezone` | string | IANA timezone identifier |
| `point` | object | Geographic coordinates |
| `point.lat` | number | Latitude coordinate |
| `point.lon` | number | Longitude coordinate |


## Location Search Best Practices

### Query Optimization

- **Minimum Length**: Queries must be at least 2 characters long
- **Language Support**: The service supports multiple languages including Turkish and English
- **Fuzzy Matching**: The search supports partial and fuzzy matching for better user experience
- **Special Characters**: Handle special characters and diacritics appropriately


### Autocomplete Implementation

- **Debouncing**: Implement debouncing to avoid excessive API calls during typing
- **Caching**: Cache recent search results to improve performance
- **Progressive Enhancement**: Show results as the user types with progressive refinement
- **Keyboard Navigation**: Support keyboard navigation for accessibility


### Location Selection

- **Place ID Usage**: Always use place IDs for location identification, not coordinates
- **Validation**: Validate selected locations before proceeding to vehicle search
- **Fallback**: Provide fallback options for locations that may not support car rentals
- **Favorites**: Consider implementing location favorites for frequent users


## Integration Examples

### Autocomplete Implementation


```javascript
class LocationAutocomplete {
    constructor(inputElement, apiToken) {
        this.input = inputElement;
        this.token = apiToken;
        this.debounceTimer = null;
        this.cache = new Map();

        this.setupEventListeners();
    }

    setupEventListeners() {
        this.input.addEventListener('input', (e) => {
            this.handleInput(e.target.value);
        });
    }

    handleInput(query) {
        // Clear previous timer
        clearTimeout(this.debounceTimer);

        // Validate minimum length
        if (query.length < 2) {
            this.clearResults();
            return;
        }

        // Debounce the search
        this.debounceTimer = setTimeout(() => {
            this.searchLocations(query);
        }, 300);
    }

    async searchLocations(query) {
        // Check cache first
        if (this.cache.has(query)) {
            this.displayResults(this.cache.get(query));
            return;
        }

        try {
            const response = await fetch(
                `/api/v1/locations?query=${encodeURIComponent(query)}`,
                {
                    headers: {
                        'Authorization': `Bearer ${this.token}`
                    }
                }
            );

            if (!response.ok) {
                throw new Error('Search failed');
            }

            const locations = await response.json();

            // Cache the results
            this.cache.set(query, locations);

            // Display results
            this.displayResults(locations);

        } catch (error) {
            console.error('Location search error:', error);
            this.showError('Failed to search locations');
        }
    }

    displayResults(locations) {
        // Implementation for displaying search results
        const resultsContainer = document.getElementById('location-results');
        resultsContainer.innerHTML = '';

        locations.forEach(location => {
            const item = document.createElement('div');
            item.className = 'location-item';
            item.innerHTML = `
        <div class="main-text">${location.mainText}</div>
        <div class="secondary-text">${location.secondaryText}</div>
      `;

            item.addEventListener('click', () => {
                this.selectLocation(location);
            });

            resultsContainer.appendChild(item);
        });
    }

    async selectLocation(location) {
        try {
            // Get detailed location information
            const response = await fetch(
                `/api/v1/locations/${location.placeId}`,
                {
                    headers: {
                        'Authorization': `Bearer ${this.token}`
                    }
                }
            );

            if (!response.ok) {
                throw new Error('Failed to get location details');
            }

            const locationDetails = await response.json();

            // Update input and trigger selection event
            this.input.value = location.description;
            this.input.dispatchEvent(new CustomEvent('locationSelected', {
                detail: locationDetails
            }));

            this.clearResults();

        } catch (error) {
            console.error('Location selection error:', error);
            this.showError('Failed to select location');
        }
    }

    clearResults() {
        const resultsContainer = document.getElementById('location-results');
        if (resultsContainer) {
            resultsContainer.innerHTML = '';
        }
    }

    showError(message) {
        // Implementation for showing error messages
        console.error(message);
    }
}

// Usage
const pickupInput = document.getElementById('pickup-location');
const autocomplete = new LocationAutocomplete(pickupInput, accessToken);

pickupInput.addEventListener('locationSelected', (event) => {
    const location = event.detail;
    console.log('Selected location:', location);

    // Store coordinates for vehicle search
    window.pickupCoordinates = {
        lat: location.point.lat,
        lon: location.point.lon
    };
});
```

### Location Validation


```javascript
async function validateLocation(placeId) {
    try {
        const response = await fetch(`/api/v1/locations/${placeId}`, {
            headers: {
                'Authorization': `Bearer ${accessToken}`
            }
        });

        if (!response.ok) {
            if (response.status === 404) {
                throw new Error('Location not found or not supported');
            }
            throw new Error('Location validation failed');
        }

        const location = await response.json();

        // Validate that location has required fields
        if (!location.point || !location.point.lat || !location.point.lon) {
            throw new Error('Location does not have valid coordinates');
        }

        return location;

    } catch (error) {
        console.error('Location validation error:', error);
        throw error;
    }
}
```

### Location Comparison


```javascript
function calculateDistance(location1, location2) {
    const R = 6371; // Earth's radius in kilometers
    const dLat = (location2.lat - location1.lat) * Math.PI / 180;
    const dLon = (location2.lon - location1.lon) * Math.PI / 180;

    const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
        Math.cos(location1.lat * Math.PI / 180) * Math.cos(location2.lat * Math.PI / 180) *
        Math.sin(dLon / 2) * Math.sin(dLon / 2);

    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    const distance = R * c;

    return distance; // Distance in kilometers
}

function validateLocationDistance(pickupLocation, dropoffLocation) {
    const distance = calculateDistance(
        pickupLocation.point,
        dropoffLocation.point
    );

    // Warn if locations are very far apart (>500km)
    if (distance > 500) {
        return {
            valid: true,
            warning: `Pickup and drop-off locations are ${distance.toFixed(0)}km apart`
        };
    }

    return {valid: true};
}
```

## Error Handling

### Common Error Responses

- `400 Bad Request`: Invalid query parameter or missing required fields
- `401 Unauthorized`: Authentication required or token expired
- `404 Not Found`: Place ID not found or location not supported
- `429 Too Many Requests`: Rate limit exceeded
- `500 Internal Server Error`: Service temporarily unavailable


### Example Error Response


```json
{
  "code": 1001,
  "description": "Invalid query parameter",
  "details": {
    "field": "query",
    "message": "Query must be at least 2 characters long"
  }
}
```

### Error Handling Best Practices

- **Graceful Degradation**: Provide fallback options when location search fails
- **User Feedback**: Show clear error messages to users
- **Retry Logic**: Implement exponential backoff for temporary failures
- **Validation**: Validate user input before making API calls


## Performance Considerations

### Caching Strategy

- **Search Results**: Cache search results for identical queries
- **Location Details**: Cache detailed location information
- **TTL**: Use appropriate time-to-live values (suggested: 1 hour for search, 24 hours for details)
- **Storage**: Use browser localStorage or sessionStorage for client-side caching


### Rate Limiting

Location endpoints are subject to rate limiting:

- **Search endpoint**: Maximum 60 requests per minute per user
- **Details endpoint**: Maximum 100 requests per minute per user


### Optimization Tips

- **Debouncing**: Use 300ms debounce for search input
- **Batch Requests**: If possible, batch location detail requests
- **Preloading**: Preload popular locations for faster response
- **CDN**: Use CDN for static location data if available


## Accessibility Considerations

- **Keyboard Navigation**: Support arrow keys for result navigation
- **Screen Readers**: Provide appropriate ARIA labels and descriptions
- **Focus Management**: Manage focus properly during location selection
- **Voice Input**: Support voice input for location search
- **High Contrast**: Ensure location results are visible in high contrast mode