22
33import com .vaadin .flow .component .UI ;
44import com .vaadin .flow .component .button .Button ;
5- import com .vaadin .flow .component .button .ButtonVariant ;
6- import com .vaadin .flow .component .html .H2 ;
5+ import com .vaadin .flow .component .html .Anchor ;
6+ import com .vaadin .flow .component .html .H3 ;
7+ import com .vaadin .flow .component .html .Span ;
8+ import com .vaadin .flow .component .messages .MessageList ;
9+ import com .vaadin .flow .component .messages .MessageListItem ;
710import com .vaadin .flow .component .notification .Notification ;
8- import com .vaadin .flow .component .orderedlayout .HorizontalLayout ;
11+ import com .vaadin .flow .component .orderedlayout .FlexLayout ;
912import com .vaadin .flow .component .orderedlayout .VerticalLayout ;
1013import com .vaadin .flow .router .Route ;
1114import io .github .makbn .jlmap .map .JLMapProvider ;
1417import org .slf4j .Logger ;
1518import org .slf4j .LoggerFactory ;
1619
20+ import java .time .Instant ;
21+ import java .util .ArrayList ;
22+ import java .util .List ;
1723import java .util .concurrent .Executors ;
1824import java .util .concurrent .ScheduledExecutorService ;
1925import java .util .concurrent .TimeUnit ;
@@ -89,14 +95,18 @@ public class MyTripToCanada extends VerticalLayout {
8995 private JLMapView mapView ;
9096 private JLMarker currentMarker ;
9197 private JLPolyline currentPath ;
98+ private final List <MessageListItem > messages = new ArrayList <>();
99+ private MessageList messageList ;
92100
93101 public MyTripToCanada () {
94- setWidthFull ();
95- setHeight ("100px" );
102+ setSizeFull ();
103+ setPadding (false );
104+ setSpacing (false );
96105
97-
98- // Title
99- H2 title = new H2 ("JL-Map Vaadin Demo" );
106+ // Main content layout with map
107+ FlexLayout mainContent = new FlexLayout ();
108+ mainContent .setSizeFull ();
109+ mainContent .getStyle ().set ("position" , "relative" );
100110
101111 // Create the map view
102112 mapView = JLMapView .builder ()
@@ -105,44 +115,122 @@ public MyTripToCanada() {
105115 .parameter (new JLMapOption .Parameter ("initialZoom" , "4" ))
106116 .build ())
107117 .startCoordinate (SARI )
108- .showZoomController (true )
118+ .showZoomController (false )
109119 .build ();
110- mapView .getStyle ().set ("border-radius" , "16px" );
111-
112- // Control panel
113- HorizontalLayout controlPanel = new HorizontalLayout ();
114- controlPanel .setSpacing (true );
115- controlPanel .setHeight ("280px" );
120+ mapView .setSizeFull ();
121+
122+ // Create menu overlay (similar to HomeView)
123+ VerticalLayout menuWrapper = new VerticalLayout ();
124+ menuWrapper .setClassName ("jlmap-menu" );
125+
126+ menuWrapper .setPadding (false );
127+ menuWrapper .setSpacing (false );
128+ menuWrapper .setWidth (null );
129+ menuWrapper .setMaxHeight ("450px" );
130+ menuWrapper .setMinWidth ("280px" );
131+ menuWrapper .setMaxWidth ("350px" );
132+ menuWrapper .setHeight (null );
133+ menuWrapper .getStyle ()
134+ .set ("position" , "absolute" )
135+ .set ("bottom" , "20px" )
136+ .set ("top" , "80% !important" )
137+ .set ("left" , "20px" )
138+ .set ("z-index" , "1000" )
139+ .set ("background" , "rgba(255, 255, 255, 0.95)" )
140+ .set ("border-radius" , "8px" )
141+ .set ("box-shadow" , "0 4px 12px rgba(0,0,0,0.15)" )
142+ .set ("padding" , "16px" );
143+
144+ // Menu content
145+ VerticalLayout menuContent = new VerticalLayout ();
146+ menuContent .setPadding (false );
147+ menuContent .setSpacing (false );
148+ menuContent .setWidthFull ();
116149
150+ // Title
151+ Span menuTitle = new Span ("Java Leaflet: Vaadin" );
152+ menuTitle .getStyle ()
153+ .set ("font-size" , "1.1em" )
154+ .set ("font-weight" , "600" )
155+ .set ("color" , "var(--lumo-header-text-color)" )
156+ .set ("display" , "block" )
157+ .set ("margin-bottom" , "12px" );
158+
159+ // Buttons
117160 Button startButton = new Button ("✈️ Start Journey" );
161+ startButton .setWidthFull ();
162+ startButton .getStyle ().set ("margin-bottom" , "8px" );
118163 startButton .addClickListener (e -> {
119164 startButton .setEnabled (false );
165+ clearMessages ();
120166 startJourney ();
121167 });
122- startButton .addThemeVariants (ButtonVariant .LUMO_PRIMARY );
123168
124169 Button resetButton = new Button ("🔄 Reset" );
125- resetButton .addThemeVariants (ButtonVariant .LUMO_PRIMARY );
126-
170+ resetButton .setWidthFull ();
127171 resetButton .addClickListener (e -> {
128172 resetJourney ();
173+ clearMessages ();
129174 startButton .setEnabled (true );
130175 });
131176
132- controlPanel .add (title , startButton , resetButton );
133-
134- add (controlPanel );
135- addAndExpand (mapView );
177+ // GitHub footer
178+ Anchor githubLink = new Anchor ("https://github.com/makbn/java_leaflet" , "" );
179+ githubLink .setTarget ("_blank" );
180+ githubLink .getStyle ()
181+ .set ("display" , "flex" )
182+ .set ("align-items" , "center" )
183+ .set ("justify-content" , "center" )
184+ .set ("margin-top" , "12px" )
185+ .set ("padding-top" , "12px" )
186+ .set ("border-top" , "1px solid var(--lumo-contrast-10pct)" )
187+ .set ("color" , "var(--lumo-secondary-text-color)" )
188+ .set ("text-decoration" , "none" )
189+ .set ("font-size" , "0.875em" );
190+ githubLink .getElement ().setProperty ("innerHTML" ,
191+ "<svg width='16' height='16' viewBox='0 0 16 16' fill='currentColor' style='margin-right: 6px;'><path d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.01.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.11.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.19 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z'/></svg>" +
192+ "<span>View on GitHub</span>" );
193+
194+ menuContent .add (menuTitle , startButton , resetButton , githubLink );
195+ menuWrapper .add (menuContent );
196+
197+ // Message list panel
198+ VerticalLayout messagePanel = new VerticalLayout ();
199+ messagePanel .setWidth ("380px" );
200+ messagePanel .getStyle ()
201+ .set ("border-left" , "1px solid var(--lumo-contrast-10pct)" );
202+ messagePanel .setPadding (false );
203+ messagePanel .setSpacing (false );
204+
205+ H3 messageTitle = new H3 ("📝 Journey Log" );
206+ messageTitle .getStyle ()
207+ .set ("margin" , "0" )
208+ .set ("padding" , "var(--lumo-space-m)" );
209+
210+ messageList = new MessageList ();
211+ messageList .setItems (messages );
212+
213+ messagePanel .add (messageTitle , messageList );
214+ messagePanel .expand (messageList );
215+
216+ // Add components to main content
217+ mainContent .add (mapView , messagePanel );
218+ mainContent .expand (mapView );
219+
220+ // Add menu overlay on top of map
221+ mapView .getElement ().appendChild (menuWrapper .getElement ());
222+
223+ add (mainContent );
136224 }
137225
138226 private void startJourney () {
139227 log .info ("Starting journey animation" );
140228 resetJourney ();
141229
142- Notification . show ( "Starting journey from Sari, Iran... " , 2000 , Notification . Position . BOTTOM_CENTER );
230+ addMessage ( "🎬" , "Journey begins! Buckle up for an adventure! " , "#9C27B0" );
143231 log .info ("About to animate first segment: Sari to Tehran" );
144232
145- // Step 1: Car from Sari to Tehran (5 seconds)
233+ // Step 1: Car from Sari to Tehran (3 seconds)
146234 animateSegment (
147235 SARI ,
148236 TEHRAN ,
@@ -153,11 +241,12 @@ private void startJourney() {
153241 "Sari, Iran" ,
154242 "Tehran, Iran" ,
155243 () -> {
156- // Step 2: Briefcase and passport (1 second)
157- Notification .show ("Arriving in Tehran - Getting ready to fly..." , 2000 , Notification .Position .BOTTOM_CENTER );
244+ addMessage ("🚗" , "Departed from beautiful Sari! Driving through scenic routes to Tehran..." , "#FF5722" );
245+ // Step 2: Briefcase and passport (1.5 seconds)
246+ addMessage ("🏙️" , "Arrived in Tehran! Time to pack my bags and grab my passport!" , "#FF9800" );
158247 showTransition (TEHRAN , BRIEFCASE_ICON , 1500 , () -> {
159- // Step 3: Airplane Tehran to Doha (3 seconds)
160- Notification . show ( "Flying to Doha..." , 2000 , Notification . Position . BOTTOM_CENTER );
248+ // Step 3: Airplane Tehran to Doha (4 seconds)
249+ addMessage ( "✈️" , "Taking off from Tehran! Soaring through the clouds to Doha..." , "#2196F3" );
161250 animateSegment (
162251 TEHRAN ,
163252 DOHA ,
@@ -168,11 +257,11 @@ private void startJourney() {
168257 "Tehran, Iran" ,
169258 "Doha, Qatar" ,
170259 () -> {
171- // Step 4: Change airplane animation (same position )
172- Notification . show ( "Transit in Doha... " , 2000 , Notification . Position . BOTTOM_CENTER );
260+ // Step 4: Transit in Doha (1.5 seconds )
261+ addMessage ( "🛬" , "Landed in Doha! Quick layover for coffee and passport check ☕ " , "#00BCD4" );
173262 showTransition (DOHA , PASSPORT_ICON , 1500 , () -> {
174263 // Step 5: Airplane Doha to Montreal (5 seconds)
175- Notification . show ( "Flying to Montreal, Canada... " , 2000 , Notification . Position . BOTTOM_CENTER );
264+ addMessage ( "🌍" , "Crossing the Atlantic! Long flight ahead but Canada awaits! 🇨🇦 " , "#2196F3" );
176265 animateSegment (
177266 DOHA ,
178267 MONTREAL ,
@@ -183,11 +272,11 @@ private void startJourney() {
183272 "Doha, Qatar" ,
184273 "Montreal, Canada" ,
185274 () -> {
186- // Step 6: Paper document (1 second )
187- Notification . show ( "Customs in Montreal..." , 2000 , Notification . Position . BOTTOM_CENTER );
275+ // Step 6: Customs in Montreal (1.5 seconds )
276+ addMessage ( "🍁" , "Bonjour Montreal! Going through customs and immigration ..." , "#4CAF50" );
188277 showTransition (MONTREAL , DOCUMENT_ICON , 1500 , () -> {
189- // Step 7: Red airplane Montreal to Calgary (4 seconds)
190- Notification . show ( " Domestic flight to Calgary... " , 2000 , Notification . Position . BOTTOM_CENTER );
278+ // Step 7: Domestic flight to Calgary (4 seconds)
279+ addMessage ( "🛫" , " Domestic flight time! Heading west to the Rockies! " , "#E91E63" );
191280 animateSegment (
192281 MONTREAL ,
193282 CALGARY ,
@@ -198,12 +287,14 @@ private void startJourney() {
198287 "Montreal, Canada" ,
199288 "Calgary, Canada" ,
200289 () -> {
201- // Step 8: House at Calgary
202- showTransition (CALGARY , HOUSE_ICON , 2000 , () ->
203- Notification .show ("🎉 Welcome to Calgary, Canada! Journey Complete!" ,
204- 5000 ,
205- Notification .Position .TOP_CENTER )
206- );
290+ // Step 8: Arrived in Calgary
291+ addMessage ("🏠" , "FINALLY HOME in Calgary! What an amazing journey! 🎉" , "#4CAF50" );
292+ showTransition (CALGARY , HOUSE_ICON , 2000 , () -> {
293+ addMessage ("🎊" , "Journey complete! Time to rest and enjoy the Rockies! 🏔️" , "#9C27B0" );
294+ Notification .show ("🎉 Welcome to Calgary, Canada! Journey Complete!" ,
295+ 5000 ,
296+ Notification .Position .TOP_CENTER );
297+ });
207298 }
208299 );
209300 });
@@ -221,6 +312,13 @@ private void animateSegment(JLLatLng start, JLLatLng end, JLIcon icon, String pa
221312 int duration , int zoomLevel , String departureName , String destinationName ,
222313 Runnable onComplete ) {
223314 log .info ("Animating segment from {} to {} with icon {}" , start , end , icon );
315+ // Fly to show the route
316+ JLLatLng midPoint = new JLLatLng (
317+ (start .getLat () + end .getLat ()) / 2 ,
318+ (start .getLng () + end .getLng ()) / 2
319+ );
320+ log .info ("Flying to midpoint: {}" , midPoint );
321+ mapView .getControlLayer ().flyTo (midPoint , zoomLevel );
224322
225323 // Add popup at departure
226324 JLPopup departurePopup = mapView .getUiLayer ().addPopup (start ,
@@ -236,26 +334,6 @@ private void animateSegment(JLLatLng start, JLLatLng end, JLIcon icon, String pa
236334 JLLatLng [] pathPoints = createCurvedPath (start , end , 300 );
237335 log .info ("Created path with {} points" , pathPoints .length );
238336
239- currentPath = mapView .getVectorLayer ().addPolyline (
240- pathPoints ,
241- JLOptions .DEFAULT .toBuilder ()
242- .color (JLColor .fromHex (pathColor ))
243- .fillColor (JLColor .fromHex ("#00FFFFFF" ))
244- .color (JLColor .fromHex ("#00FFFFFF" ))
245- .fill (false )
246- .weight (4 )
247- .opacity (0.7 )
248- .build ()
249- );
250- log .info ("Added polyline to map" );
251-
252- // Fly to show the route
253- JLLatLng midPoint = new JLLatLng (
254- (start .getLat () + end .getLat ()) / 2 ,
255- (start .getLng () + end .getLng ()) / 2
256- );
257- log .info ("Flying to midpoint: {}" , midPoint );
258- mapView .getControlLayer ().flyTo (midPoint , zoomLevel );
259337
260338 // Add popup at destination
261339 JLPopup destinationPopup = mapView .getUiLayer ().addPopup (end ,
@@ -299,6 +377,21 @@ private void animateMarkerAlongPath(JLIcon icon, JLLatLng[] path, int duration,
299377
300378 executor .scheduleAtFixedRate (() -> {
301379 int step = currentStep .getAndIncrement ();
380+ if (step == 1 ) {
381+ ui .access (() -> {
382+ currentPath = mapView .getVectorLayer ().addPolyline (
383+ path ,
384+ JLOptions .DEFAULT .toBuilder ()
385+ .fillColor (JLColor .fromHex ("#00FFFFFF" ))
386+ .color (JLColor .fromHex ("#00FFFFFF" ))
387+ .fill (false )
388+ .weight (4 )
389+ .opacity (0.7 )
390+ .build ()
391+ );
392+ log .info ("Added polyline to map" );
393+ });
394+ }
302395
303396 if (step <= totalSteps ) {
304397 // Calculate which point in the path to use
@@ -323,7 +416,7 @@ private void animateMarkerAlongPath(JLIcon icon, JLLatLng[] path, int duration,
323416 ui .access (onComplete ::run );
324417 }
325418 }
326- }, 0 , delayPerStep , TimeUnit .MILLISECONDS );
419+ }, 1000 , delayPerStep , TimeUnit .MILLISECONDS );
327420 }
328421
329422 private void showTransition (JLLatLng position , JLIcon icon , int duration , Runnable onComplete ) {
@@ -391,4 +484,22 @@ private void resetJourney() {
391484 // Reset view to starting position
392485 mapView .getControlLayer ().flyTo (SARI , 4 );
393486 }
487+
488+ private void addMessage (String icon , String message , String color ) {
489+ UI .getCurrent ().access (() -> {
490+ MessageListItem item = new MessageListItem (
491+ icon + " " + message ,
492+ Instant .now (),
493+ "Journey Bot"
494+ );
495+
496+ messages .add (item );
497+ messageList .setItems (messages );
498+ });
499+ }
500+
501+ private void clearMessages () {
502+ messages .clear ();
503+ messageList .setItems (messages );
504+ }
394505}
0 commit comments