# 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 = `
${location.mainText}
${location.secondaryText}
`; 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