PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.8
Yatra – Travel Booking & Tour Operator Software v3.0.2.8
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / templates / partials / single-trip / content-location.php

content-location.php in Yatra – Travel Booking & Tour Operator Software 3.0.2.8, at templates/partials/single-trip/content-location.php

872 lines 49.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if (!defined('ABSPATH')) {
3 exit;
4 }
5
6 // Location Section
7 // Expected variables: $trip, $tab, $itinerary_entries
8 ?>
9 <section class="yatra-trip-section" id="location" itemscope itemtype="https://schema.org/Place">
10 <h2 class="yatra-trip-section-title">
11 <?php yatra_render_tab_icon($tab->icon ?? null, 'map-pin', 'yatra-trip-section-title-icon', $tab->label ?? 'Location'); ?>
12 <?php echo esc_html(isset($tab->label) ? $tab->label : __('Location', 'yatra')); ?>
13 </h2>
14
15 <?php
16 // Use starting location coordinates if available
17 $map_lat = $trip->getStartingLatitude() ?? null;
18 $map_lng = $trip->getStartingLongitude() ?? null;
19
20 // Itinerary entries with coordinates are passed from controller (proper MVC pattern)
21 // This follows the separation of concerns principle
22 ?>
23
24 <?php if (!empty($map_lat) && !empty($map_lng)): ?>
25 <div class="yatra-trip-map" id="yatra-trip-map"
26 data-lat="<?php echo esc_attr($map_lat); ?>"
27 data-lng="<?php echo esc_attr($map_lng); ?>"
28 data-starting-location="<?php echo esc_attr($trip->getStartingLocation() ?? ''); ?>"
29 itemprop="hasMap" itemscope itemtype="https://schema.org/Map"
30 style="position: relative;">
31 <meta itemprop="mapType" content="OpenStreetMap">
32 <div id="yatra-openstreet-map" style="width: 100%; height: 400px; border-radius: 12px; background: white; overflow: hidden;"></div>
33 </div>
34 <div itemprop="geo" itemscope itemtype="https://schema.org/GeoCoordinates">
35 <meta itemprop="latitude" content="<?php echo esc_attr($map_lat); ?>">
36 <meta itemprop="longitude" content="<?php echo esc_attr($map_lng); ?>">
37 </div>
38
39 <!-- Load Leaflet CSS and JS -->
40 <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
41 integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
42 crossorigin=""/>
43 <script src="https://unpkg.com/[email protected]/dist/leaflet.js"
44 integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
45 crossorigin=""></script>
46
47 <script>
48 /**
49 * Yatra Trip Map Initialization
50 * Renders interactive map with trip route, markers, and directional arrows
51 * @version 1.0.0
52 */
53 (function() {
54 'use strict';
55
56 // Wait for DOM and Leaflet to be ready
57 if (typeof L === 'undefined') {
58 console.error('[Yatra Map] Leaflet library not loaded');
59 return;
60 }
61
62 document.addEventListener('DOMContentLoaded', function() {
63 try {
64 // Initialize map only if the map container exists
65 var mapContainer = document.getElementById('yatra-openstreet-map');
66 if (!mapContainer) {
67 console.warn('[Yatra Map] Map container not found');
68 return;
69 }
70
71 // Parse and validate coordinates
72 var startLat = parseFloat('<?php echo esc_js($trip->getStartingLatitude() ?? '0'); ?>');
73 var startLng = parseFloat('<?php echo esc_js($trip->getStartingLongitude() ?? '0'); ?>');
74 var endLat = parseFloat('<?php echo esc_js($trip->ending_latitude ?? '0'); ?>');
75 var endLng = parseFloat('<?php echo esc_js($trip->ending_longitude ?? '0'); ?>');
76 var startingLocation = '<?php echo esc_js($trip->getStartingLocation() ?? ''); ?>';
77 var endingLocation = '<?php echo esc_js($trip->ending_location ?? ''); ?>';
78
79 // Validate coordinates
80 if (isNaN(startLat) || isNaN(startLng) || startLat === 0 || startLng === 0) {
81 console.warn('[Yatra Map] Invalid starting coordinates');
82 mapContainer.innerHTML = '<div style="padding: 40px; text-align: center; color: #64748b;">Map location not available</div>';
83 return;
84 }
85
86 // Itinerary activities with coordinates (sanitized)
87 var activities = <?php echo wp_json_encode(array_map(function($entry) {
88 return [
89 'id' => absint($entry->id),
90 'title' => sanitize_text_field($entry->title),
91 'location' => sanitize_text_field($entry->location ?? ''),
92 'lat' => floatval($entry->location_latitude),
93 'lng' => floatval($entry->location_longitude),
94 'day' => absint($entry->day_number),
95 'day_title' => sanitize_text_field($entry->day_title ?? ''),
96 'start_time' => sanitize_text_field($entry->start_time ?? ''),
97 'end_time' => sanitize_text_field($entry->end_time ?? ''),
98 'description' => wp_kses_post($entry->description ?? '')
99 ];
100 }, $itinerary_entries ?? []), JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;
101
102 // Validate activities data
103 if (!Array.isArray(activities)) {
104 console.warn('[Yatra Map] Invalid activities data');
105 activities = [];
106 }
107
108 // Calculate center point for better map view
109 var centerLat = (startLat + endLat) / 2;
110 var centerLng = (startLng + endLng) / 2;
111 var mapLat = startLat || centerLat;
112 var mapLng = startLng || centerLng;
113
114 // Initialize map with controls and error handling
115 var map = L.map('yatra-openstreet-map', {
116 center: [startLat || 0, startLng || 0],
117 zoom: 13,
118 zoomControl: true,
119 scrollWheelZoom: true,
120 doubleClickZoom: true,
121 touchZoom: true,
122 dragging: true,
123 tap: true,
124 maxZoom: 19,
125 minZoom: 2
126 });
127
128 // Add scale control
129 L.control.scale().addTo(map);
130
131 // Add attribution control
132 map.attributionControl.setPrefix('');
133
134 // Add OpenStreetMap tiles with error handling
135 var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
136 attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
137 maxZoom: 19,
138 minZoom: 2,
139 errorTileUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
140 crossOrigin: true
141 });
142
143 tileLayer.on('tileerror', function(error) {
144 console.warn('[Yatra Map] Tile loading error:', error);
145 });
146
147 tileLayer.addTo(map);
148
149 // Create custom icons with SVG
150 var startIcon = L.divIcon({
151 html: '<div style="background: linear-gradient(135deg, #10b981, #059669); color: white; width: 30px; height: 30px; border-radius: 50% 50% 50% 0; transform: rotate(-45deg); display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 6px rgba(0,0,0,0.3);"><svg style="transform: rotate(45deg); width: 14px; height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div>',
152 iconSize: [30, 30],
153 iconAnchor: [15, 30],
154 popupAnchor: [0, -30],
155 className: 'yatra-start-marker'
156 });
157
158 var endIcon = L.divIcon({
159 html: '<div style="background: linear-gradient(135deg, #ef4444, #dc2626); color: white; width: 30px; height: 30px; border-radius: 50% 50% 50% 0; transform: rotate(-45deg); display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 6px rgba(0,0,0,0.3);"><svg style="transform: rotate(45deg); width: 14px; height: 14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></div>',
160 iconSize: [30, 30],
161 iconAnchor: [15, 30],
162 popupAnchor: [0, -30],
163 className: 'yatra-end-marker'
164 });
165
166 // Activity marker icon
167 var activityIcon = L.divIcon({
168 html: '<div style="background: linear-gradient(135deg, #3b82f6, #2563eb); color: white; width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 2px solid white; box-shadow: 0 2px 6px rgba(0,0,0,0.3);"><svg width="12px" height="12px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg></div>',
169 iconSize: [26, 26],
170 iconAnchor: [13, 26],
171 popupAnchor: [0, -26],
172 className: 'yatra-activity-marker'
173 });
174
175 var markers = [];
176 var routeCoordinates = [];
177
178 // Helper function to escape HTML
179 function escapeHtml(text) {
180 var div = document.createElement('div');
181 div.textContent = text;
182 return div.innerHTML;
183 }
184
185 // Helper function to truncate text
186 function truncateText(text, maxLength) {
187 if (!text) return '';
188 return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
189 }
190
191 // Add marker for starting location
192 if (startLat && startLng && !isNaN(startLat) && !isNaN(startLng)) {
193 var startMarker = L.marker([startLat, startLng], { icon: startIcon }).addTo(map);
194 var startPopupContent = '<div class="yatra-popup-content">' +
195 '<div class="yatra-popup-header">' +
196 '<div class="yatra-popup-icon-start">' +
197 '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12l5 5L20 7"/></svg>' +
198 '</div>' +
199 '<div class="yatra-popup-title-section">' +
200 '<h4 class="yatra-popup-title">' + (startingLocation || 'Trip Starting Point') + '</h4>' +
201 '<span class="yatra-popup-badge yatra-badge-start">Starting Point</span>' +
202 '</div>' +
203 '</div>' +
204 '<div class="yatra-popup-body">' +
205 '<div class="yatra-popup-info">' +
206 '<div class="yatra-popup-info-item">' +
207 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>Location:</span>' +
208 '<span class="yatra-popup-value">' + (startingLocation || 'Trip Start') + '</span>' +
209 '</div>' +
210 '<div class="yatra-popup-info-item">' +
211 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>Coordinates:</span>' +
212 '<span class="yatra-popup-value">' + startLat.toFixed(6) + ', ' + startLng.toFixed(6) + '</span>' +
213 '</div>' +
214 '<div class="yatra-popup-info-item">' +
215 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><polygon points="1 6 1 22 8 18 16 22 16 6"/><polyline points="8 18 8 12 16 12"/></svg>Map:</span>' +
216 '<a href="https://www.openstreetmap.org/?mlat=' + startLat + '&mlon=' + startLng + '#map=15/' + startLat + '/' + startLng + '" target="_blank" class="yatra-popup-link">View on OpenStreetMap ↗</a>' +
217 '</div>' +
218 '</div>' +
219 '</div>' +
220 '</div>';
221 startMarker.bindPopup(startPopupContent, {
222 maxWidth: 300,
223 className: 'yatra-custom-popup'
224 });
225
226 // Add tooltip that shows on hover
227 var startTooltip = startingLocation ? startingLocation : 'Trip Starting Point';
228 startMarker.bindTooltip(startTooltip, {
229 permanent: false,
230 direction: 'top',
231 className: 'yatra-marker-tooltip',
232 offset: [0, -35]
233 });
234
235 markers.push(startMarker);
236 routeCoordinates.push([startLat, startLng]);
237 }
238
239 // Add markers for all itinerary activities with validation
240 activities.forEach(function(activity, index) {
241 if (!activity || typeof activity !== 'object') {
242 console.warn('[Yatra Map] Invalid activity data at index:', index);
243 return;
244 }
245
246 if (activity.lat && activity.lng && !isNaN(activity.lat) && !isNaN(activity.lng)) {
247 var activityMarker = L.marker([activity.lat, activity.lng], { icon: activityIcon }).addTo(map);
248
249 // Create comprehensive activity popup content
250 var activityPopupContent = '<div class="yatra-popup-content">' +
251 '<div class="yatra-popup-header">' +
252 '<div class="yatra-popup-icon-activity">' +
253 '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>' +
254 '</div>' +
255 '<div class="yatra-popup-title-section">' +
256 '<h4 class="yatra-popup-title">' + activity.title + '</h4>' +
257 '<div class="yatra-popup-badges">' +
258 '<span class="yatra-popup-badge yatra-badge-activity">Day ' + activity.day + '</span>' +
259 (activity.day_title ? '<span class="yatra-popup-day-title">' + activity.day_title + '</span>' : '') +
260 '</div>' +
261 '</div>' +
262 '</div>' +
263 '<div class="yatra-popup-body">' +
264 '<div class="yatra-popup-info">' +
265 (activity.location ? '<div class="yatra-popup-info-item">' +
266 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>Location:</span>' +
267 '<span class="yatra-popup-value">' + activity.location + '</span>' +
268 '</div>' : '') +
269 (activity.start_time && activity.end_time ? '<div class="yatra-popup-info-item">' +
270 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>Time:</span>' +
271 '<span class="yatra-popup-value">' + activity.start_time + ' - ' + activity.end_time + '</span>' +
272 '</div>' : '') +
273 '<div class="yatra-popup-info-item">' +
274 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>Coordinates:</span>' +
275 '<span class="yatra-popup-value">' + activity.lat.toFixed(6) + ', ' + activity.lng.toFixed(6) + '</span>' +
276 '</div>' +
277 '<div class="yatra-popup-info-item">' +
278 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><polygon points="1 6 1 22 8 18 16 22 16 6"/><polyline points="8 18 8 12 16 12"/></svg>Map:</span>' +
279 '<a href="https://www.openstreetmap.org/?mlat=' + activity.lat + '&mlon=' + activity.lng + '#map=15/' + activity.lat + '/' + activity.lng + '" target="_blank" class="yatra-popup-link">View on OpenStreetMap ↗</a>' +
280 '</div>' +
281 '</div>' +
282 (activity.description ? '<div class="yatra-popup-description">' +
283 '<div class="yatra-popup-desc-title"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>Description</div>' +
284 '<div class="yatra-popup-desc-text">' +
285 (activity.description.length > 150 ? activity.description.substring(0, 150) + '...' : activity.description) +
286 '</div>' +
287 '</div>' : '') +
288 '</div>' +
289 '</div>';
290
291 activityMarker.bindPopup(activityPopupContent, {
292 maxWidth: 320,
293 className: 'yatra-custom-popup'
294 });
295
296 // Add tooltip that shows on hover
297 var activityTooltip = '<strong>' + activity.title + '</strong>';
298 if (activity.location) {
299 activityTooltip += '<br><small>' + activity.location + '</small>';
300 }
301 activityMarker.bindTooltip(activityTooltip, {
302 permanent: false,
303 direction: 'top',
304 className: 'yatra-marker-tooltip',
305 offset: [0, -32]
306 });
307
308 markers.push(activityMarker);
309 routeCoordinates.push([activity.lat, activity.lng]);
310 }
311 });
312
313 // Add marker for ending location
314 if (endLat && endLng && !isNaN(endLat) && !isNaN(endLng)) {
315 var endMarker = L.marker([endLat, endLng], { icon: endIcon }).addTo(map);
316 var endPopupContent = '<div class="yatra-popup-content">' +
317 '<div class="yatra-popup-header">' +
318 '<div class="yatra-popup-icon-end">' +
319 '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>' +
320 '</div>' +
321 '<div class="yatra-popup-title-section">' +
322 '<h4 class="yatra-popup-title">' + (endingLocation || 'Trip Ending Point') + '</h4>' +
323 '<span class="yatra-popup-badge yatra-badge-end">Ending Point</span>' +
324 '</div>' +
325 '</div>' +
326 '<div class="yatra-popup-body">' +
327 '<div class="yatra-popup-info">' +
328 '<div class="yatra-popup-info-item">' +
329 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>Location:</span>' +
330 '<span class="yatra-popup-value">' + (endingLocation || 'Trip End') + '</span>' +
331 '</div>' +
332 '<div class="yatra-popup-info-item">' +
333 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>Coordinates:</span>' +
334 '<span class="yatra-popup-value">' + endLat.toFixed(6) + ', ' + endLng.toFixed(6) + '</span>' +
335 '</div>' +
336 '<div class="yatra-popup-info-item">' +
337 '<span class="yatra-popup-label"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align: middle; margin-right: 4px;"><polygon points="1 6 1 22 8 18 16 22 16 6"/><polyline points="8 18 8 12 16 12"/></svg>Map:</span>' +
338 '<a href="https://www.openstreetmap.org/?mlat=' + endLat + '&mlon=' + endLng + '#map=15/' + endLat + '/' + endLng + '" target="_blank" class="yatra-popup-link">View on OpenStreetMap ↗</a>' +
339 '</div>' +
340 '</div>' +
341 '</div>' +
342 '</div>';
343 endMarker.bindPopup(endPopupContent, {
344 maxWidth: 300,
345 className: 'yatra-custom-popup'
346 });
347
348 // Add tooltip that shows on hover
349 var endTooltip = endingLocation ? endingLocation : 'Trip Ending Point';
350 endMarker.bindTooltip(endTooltip, {
351 permanent: false,
352 direction: 'top',
353 className: 'yatra-marker-tooltip',
354 offset: [0, -35]
355 });
356
357 markers.push(endMarker);
358 routeCoordinates.push([endLat, endLng]);
359 }
360
361 // Draw complete travel route connecting all locations
362 if (routeCoordinates.length >= 2) {
363 // Route is ready for visualization
364
365 // Main route line with better visibility
366 var routeLine = L.polyline(routeCoordinates, {
367 color: '#3b82f6',
368 weight: 5,
369 opacity: 0.8,
370 smoothFactor: 1,
371 lineCap: 'round',
372 lineJoin: 'round'
373 }).addTo(map);
374
375 // Add animated dashed overlay for travel effect
376 var dashedLine = L.polyline(routeCoordinates, {
377 color: '#60a5fa',
378 weight: 3,
379 opacity: 0.9,
380 dashArray: '10, 15',
381 dashOffset: '0',
382 className: 'animated-route'
383 }).addTo(map);
384
385 // Add directional arrows to show travel direction with distance-based distribution
386 for (var i = 0; i < routeCoordinates.length - 1; i++) {
387 var start = routeCoordinates[i];
388 var end = routeCoordinates[i + 1];
389
390 // Calculate distance between points (simplified haversine formula)
391 var deltaLat = end[0] - start[0];
392 var deltaLng = end[1] - start[1];
393 var distance = Math.sqrt(deltaLat * deltaLat + deltaLng * deltaLng) * 111000; // Approximate distance in meters
394
395 // Determine number of arrows based on distance (reduced for cleaner look)
396 var arrowCount;
397 if (distance < 1000) {
398 arrowCount = 1; // Short distance: 1 arrow
399 } else if (distance < 3000) {
400 arrowCount = 1; // Medium distance: still 1 arrow
401 } else if (distance < 8000) {
402 arrowCount = 2; // Long distance: 2 arrows
403 } else {
404 arrowCount = 2; // Very long distance: max 2 arrows
405 }
406
407 // Place arrows evenly along the segment
408 for (var j = 0; j < arrowCount; j++) {
409 var fraction = (j + 1) / (arrowCount + 1); // Even distribution
410 var pointLat = start[0] + deltaLat * fraction;
411 var pointLng = start[1] + deltaLng * fraction;
412
413 // Calculate angle for arrow direction
414 var angleRad = Math.atan2(deltaLng, deltaLat);
415 var angleDeg = angleRad * 180 / Math.PI;
416
417 var arrowIcon = L.divIcon({
418 html: '<div style="width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center;">' +
419 '<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#3b82f6" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" style="transform: rotate(' + angleDeg + 'deg);"><polyline points="18 15 12 9 6 15"/></svg>' +
420 '</div>',
421 iconSize: [32, 32],
422 iconAnchor: [16, 16],
423 className: 'yatra-route-arrow'
424 });
425
426 var arrowMarker = L.marker([pointLat, pointLng], { icon: arrowIcon }).addTo(map);
427 }
428 }
429
430 // Fit map to show all markers with padding
431 var group = new L.featureGroup(markers);
432 map.fitBounds(group.getBounds().pad(0.15));
433 } else if (markers.length > 0) {
434 // If only one marker, center on it
435 if (markers.length === 1) {
436 markers[0].openPopup();
437 } else {
438 // Fit to show all markers
439 var group = new L.featureGroup(markers);
440 map.fitBounds(group.getBounds().pad(0.15));
441 }
442 }
443
444 // Markers are now properly added and persistent
445
446 // Add CSS animation for route only
447 var style = document.createElement('style');
448 style.textContent = `
449 @keyframes dash {
450 to {
451 stroke-dashoffset: -100;
452 }
453 }
454 .yatra-custom-marker, .yatra-start-marker, .yatra-end-marker, .yatra-activity-marker {
455 filter: drop-shadow(0 2px 4px rgba(0,0,0,0.2));
456 transition: all 0.3s ease;
457 }
458 .yatra-custom-marker:hover, .yatra-start-marker:hover, .yatra-end-marker:hover, .yatra-activity-marker:hover {
459 filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3));
460 transform: scale(1.1);
461 }
462 .animated-route {
463 animation: dash 30s linear infinite;
464 }
465 .yatra-route-arrow {
466 transition: all 0.3s ease;
467 z-index: 600 !important;
468 }
469 .yatra-route-arrow:hover {
470 transform: scale(1.2);
471 filter: brightness(1.2);
472 }
473 `;
474 document.head.appendChild(style);
475
476 } catch (error) {
477 console.error('[Yatra Map] Initialization error:', error);
478 if (mapContainer) {
479 mapContainer.innerHTML = '<div style="padding: 40px; text-align: center; color: #ef4444;">Map failed to load. Please refresh the page.</div>';
480 }
481 }
482 });
483 })();
484 </script>
485 <?php else: ?>
486 <div class="yatra-trip-map yatra-map-placeholder">
487 <p><?php echo esc_html__('Map location not available', 'yatra'); ?></p>
488 </div>
489 <?php endif; ?>
490
491 <?php if (!empty($trip->starting_location) || !empty($trip->ending_location) || !empty($trip->landmarks) || (!empty($trip->starting_latitude) && !empty($trip->starting_longitude)) || (!empty($trip->ending_latitude) && !empty($trip->ending_longitude))): ?>
492 <div class="yatra-location-details" style="margin-top: 24px;">
493 <div class="yatra-location-grid">
494 <?php if (!empty($trip->starting_location)): ?>
495 <div class="yatra-location-item">
496 <div class="yatra-location-icon">
497 <?php echo yatra_svg_icon('play', 'yatra-location-item-icon'); ?>
498 </div>
499 <div class="yatra-location-content">
500 <div class="yatra-location-label"><?php echo esc_html__('Starting Point', 'yatra'); ?></div>
501 <div class="yatra-location-value">
502 <?php if (!empty($trip->starting_latitude) && !empty($trip->starting_longitude)): ?>
503 <a href="https://www.openstreetmap.org/?mlat=<?php echo esc_attr($trip->starting_latitude); ?>&mlon=<?php echo esc_attr($trip->starting_longitude); ?>#map=15/<?php echo esc_attr($trip->starting_latitude); ?>/<?php echo esc_attr($trip->starting_longitude); ?>"
504 target="_blank"
505 rel="noopener noreferrer"
506 class="yatra-coordinates-link">
507 <?php echo esc_html($trip->starting_location); ?>
508
509 </a>
510 <?php else: ?>
511 <?php echo esc_html($trip->starting_location); ?>
512 <?php endif; ?>
513 </div>
514 </div>
515 </div>
516 <?php endif; ?>
517
518 <?php if (!empty($trip->ending_location)): ?>
519 <div class="yatra-location-item">
520 <div class="yatra-location-icon">
521 <?php echo yatra_svg_icon('check', 'yatra-location-item-icon'); ?>
522 </div>
523 <div class="yatra-location-content">
524 <div class="yatra-location-label"><?php echo esc_html__('Ending Point', 'yatra'); ?></div>
525 <div class="yatra-location-value">
526 <?php if (!empty($trip->ending_latitude) && !empty($trip->ending_longitude)): ?>
527 <a href="https://www.openstreetmap.org/?mlat=<?php echo esc_attr($trip->ending_latitude); ?>&mlon=<?php echo esc_attr($trip->ending_longitude); ?>#map=15/<?php echo esc_attr($trip->ending_latitude); ?>/<?php echo esc_attr($trip->ending_longitude); ?>"
528 target="_blank"
529 rel="noopener noreferrer"
530 class="yatra-coordinates-link">
531 <?php echo esc_html($trip->ending_location); ?>
532 </a>
533 <?php else: ?>
534 <?php echo esc_html($trip->ending_location); ?>
535 <?php endif; ?>
536 </div>
537 </div>
538 </div>
539 <?php endif; ?>
540
541 <?php if (!empty($trip->landmarks) && is_array($trip->landmarks)): ?>
542 <div class="yatra-location-item yatra-location-item-landmarks">
543 <div class="yatra-location-icon">
544 <?php echo yatra_svg_icon('map-pin', 'yatra-location-item-icon'); ?>
545 </div>
546 <div class="yatra-location-content">
547 <div class="yatra-location-label"><?php echo esc_html__('Key Landmarks', 'yatra'); ?></div>
548 <div class="yatra-location-value">
549 <div class="yatra-landmarks-list">
550 <?php foreach ($trip->landmarks as $index => $landmark): ?>
551 <span class="yatra-landmark-tag">
552 <?php echo yatra_svg_icon('map-pin', 'yatra-landmark-icon'); ?>
553 <?php echo esc_html($landmark); ?>
554 </span>
555 <?php endforeach; ?>
556 </div>
557 </div>
558 </div>
559 </div>
560 <?php endif; ?>
561 </div>
562 </div>
563
564 <style>
565 /* Enhanced popup styling */
566 .yatra-custom-popup {
567 font-family: system-ui, -apple-system, sans-serif !important;
568 }
569
570 .yatra-popup-content {
571 min-width: 280px;
572 max-width: 320px;
573 }
574
575 .yatra-popup-header {
576 display: flex;
577 align-items: flex-start;
578 gap: 12px;
579 padding-bottom: 12px;
580 border-bottom: 2px solid #e2e8f0;
581 margin-bottom: 12px;
582 }
583
584 .yatra-popup-icon-start,
585 .yatra-popup-icon-activity,
586 .yatra-popup-icon-end {
587 font-size: 24px;
588 flex-shrink: 0;
589 width: 40px;
590 height: 40px;
591 display: flex;
592 align-items: center;
593 justify-content: center;
594 border-radius: 50%;
595 }
596
597 .yatra-popup-icon-start {
598 background: linear-gradient(135deg, #10b981, #059669);
599 }
600
601 .yatra-popup-icon-activity {
602 background: linear-gradient(135deg, #3b82f6, #2563eb);
603 }
604
605 .yatra-popup-icon-end {
606 background: linear-gradient(135deg, #ef4444, #dc2626);
607 }
608
609 .yatra-popup-title-section {
610 flex: 1;
611 }
612
613 .yatra-popup-title {
614 margin: 0 0 6px 0;
615 font-size: 16px;
616 font-weight: 600;
617 color: #1e293b;
618 line-height: 1.2;
619 }
620
621 .yatra-popup-badges {
622 display: flex;
623 flex-wrap: wrap;
624 gap: 6px;
625 align-items: center;
626 }
627
628 .yatra-popup-badge {
629 display: inline-block;
630 padding: 3px 8px;
631 border-radius: 12px;
632 font-size: 11px;
633 font-weight: 600;
634 text-transform: uppercase;
635 letter-spacing: 0.5px;
636 }
637
638 .yatra-badge-start {
639 background: #dcfce7;
640 color: #166534;
641 }
642
643 .yatra-badge-activity {
644 background: #dbeafe;
645 color: #1e40af;
646 }
647
648 .yatra-badge-end {
649 background: #fee2e2;
650 color: #991b1b;
651 }
652
653 .yatra-popup-day-title {
654 color: #64748b;
655 font-size: 12px;
656 font-style: italic;
657 }
658
659 .yatra-popup-body {
660
661 }
662
663 .yatra-popup-info {
664 margin-bottom: 12px;
665 }
666
667 .yatra-popup-info-item {
668 display: flex;
669 justify-content: space-between;
670 align-items: flex-start;
671 margin-bottom: 8px;
672 gap: 8px;
673 }
674
675 .yatra-popup-label {
676 color: #64748b;
677 font-size: 12px;
678 font-weight: 500;
679 flex-shrink: 0;
680 min-width: 80px;
681 }
682
683 .yatra-popup-value {
684 color: #1e293b;
685 font-size: 12px;
686 font-weight: 500;
687 text-align: right;
688 flex: 1;
689 }
690
691 .yatra-popup-link {
692 color: #3b82f6 !important;
693 text-decoration: none !important;
694 font-size: 11px !important;
695 font-weight: 500 !important;
696 transition: color 0.2s ease !important;
697 }
698
699 .yatra-popup-link:hover {
700 color: #2563eb !important;
701 text-decoration: underline !important;
702 }
703
704 .yatra-popup-description {
705 margin-top: 12px;
706 padding-top: 12px;
707 border-top: 1px solid #e2e8f0;
708 }
709
710 .yatra-popup-desc-title {
711 color: #475569;
712 font-size: 12px;
713 font-weight: 600;
714 margin-bottom: 6px;
715 }
716
717 .yatra-popup-desc-text {
718 color: #64748b;
719 font-size: 12px;
720 line-height: 1.4;
721 }
722
723 /* Tooltip styling */
724 .yatra-marker-tooltip {
725 background: rgba(30, 41, 59, 0.95) !important;
726 border: none !important;
727 border-radius: 8px !important;
728 padding: 8px 12px !important;
729 font-family: system-ui, -apple-system, sans-serif !important;
730 font-size: 13px !important;
731 font-weight: 500 !important;
732 color: white !important;
733 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
734 backdrop-filter: blur(10px) !important;
735 }
736
737 .yatra-marker-tooltip::before {
738 border-top-color: rgba(30, 41, 59, 0.95) !important;
739 }
740
741 .leaflet-tooltip-top.yatra-marker-tooltip::before {
742 border-top-color: rgba(30, 41, 59, 0.95) !important;
743 }
744
745 .yatra-marker-tooltip strong {
746 color: white !important;
747 font-weight: 600 !important;
748 }
749
750 .yatra-marker-tooltip small {
751 color: #cbd5e1 !important;
752 font-size: 11px !important;
753 }
754
755
756 .yatra-location-grid {
757 display: grid;
758 grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
759 gap: 16px;
760 margin-top: 24px;
761 }
762
763 .yatra-location-item {
764 display: flex;
765 align-items: flex-start;
766 gap: 12px;
767 padding: 16px;
768 background: #f8fafc;
769 border-radius: 8px;
770 border: 1px solid #e2e8f0;
771 transition: all 0.3s ease;
772 }
773
774 .yatra-location-item:hover {
775 background: #f1f5f9;
776 border-color: #cbd5e1;
777 transform: translateY(-2px);
778 }
779
780 .yatra-location-item-landmarks {
781 grid-column: 1 / -1;
782 }
783
784 .yatra-location-icon {
785 flex-shrink: 0;
786 width: 40px;
787 height: 40px;
788 display: flex;
789 align-items: center;
790 justify-content: center;
791 background: linear-gradient(135deg, #3b82f6, #2563eb);
792 color: white;
793 border-radius: 8px;
794 box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
795 }
796
797 .yatra-location-item-icon {
798 width: 20px;
799 height: 20px;
800 }
801
802 .yatra-location-content {
803 flex: 1;
804 }
805
806 .yatra-location-label {
807 font-size: 12px;
808 font-weight: 600;
809 color: #64748b;
810 text-transform: uppercase;
811 letter-spacing: 0.5px;
812 margin-bottom: 4px;
813 }
814
815 .yatra-location-value {
816 font-size: 14px;
817 color: #1e293b;
818 font-weight: 500;
819 }
820
821 .yatra-coordinates-link {
822 display: inline-flex;
823 align-items: center;
824 gap: 4px;
825 color: #3b82f6;
826 text-decoration: none;
827 transition: color 0.2s ease;
828 }
829
830 .yatra-coordinates-link:hover {
831 color: #2563eb;
832 text-decoration: underline;
833 }
834
835 .yatra-external-link-icon {
836 font-size: 12px;
837 opacity: 0.7;
838 }
839
840 .yatra-landmarks-list {
841 display: flex;
842 flex-wrap: wrap;
843 gap: 8px;
844 }
845
846 .yatra-landmark-tag {
847 display: inline-flex;
848 align-items: center;
849 gap: 4px;
850 padding: 4px 8px;
851 background: linear-gradient(135deg, #f0f9ff, #e0f2fe);
852 color: #0369a1;
853 border: 1px solid #bae6fd;
854 border-radius: 6px;
855 font-size: 12px;
856 font-weight: 500;
857 transition: all 0.2s ease;
858 }
859
860 .yatra-landmark-tag:hover {
861 background: linear-gradient(135deg, #e0f2fe, #bae6fd);
862 border-color: #7dd3fc;
863 }
864
865 .yatra-landmark-icon {
866 width: 12px;
867 height: 12px;
868 }
869
870 </style>
871 <?php endif; ?>
872 </section>