@yarrow-uz/yarrow-map-web-sdk 1.0.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,976 @@
1
+ # Yarrow Map Web SDK Documentation
2
+
3
+ This document provides a comprehensive guide to using the Yarrow Map Web SDK for embedding and interacting with Yarrow maps in your web application.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Getting Started](#getting-started)
8
+ - [Installation](#installation)
9
+ - [React Usage](#react-usage)
10
+ - [Initialization](#initialization)
11
+ - [Configuration Options](#configuration-options)
12
+ - [Basic Map Manipulation](#basic-map-manipulation)
13
+ - [Changing the Map Style](#changing-the-map-style)
14
+ - [Changing the Map Theme](#changing-the-map-theme)
15
+ - [Moving the Map View](#moving-the-map-view)
16
+ - [Changing Background Color](#changing-background-color)
17
+ - [Handling Events](#handling-events)
18
+ - [Listen for Map Movement](#listen-for-map-movement)
19
+ - [Listen for Map Clicks](#listen-for-map-clicks)
20
+ - [Listen for Clicks on POIs or Buildings](#listen-for-clicks-on-pois-or-buildings)
21
+ - [Working with Layers and Data](#working-with-layers-and-data)
22
+ - [Adding a GeoJSON Layer](#adding-a-geojson-layer)
23
+ - [Advanced Layer Configuration](#advanced-layer-configuration)
24
+ - [Adding and Removing Markers](#adding-and-removing-markers)
25
+ - [Removing a Layer](#removing-a-layer)
26
+ - [Routing](#routing)
27
+ - [Building and Displaying a Simple Route](#building-and-displaying-a-simple-route)
28
+ - [Building and Displaying a Multi-Stop Route](#building-and-displaying-a-multi-stop-route)
29
+ - [Clearing Routes](#clearing-routes)
30
+ - [Rendering Multiple Routes](#rendering-multiple-routes)
31
+ - [Search](#search)
32
+ - [Highlighting Search Results](#highlighting-search-results)
33
+ - [Advanced Search Configuration](#advanced-search-configuration)
34
+ - [Public Transport](#public-transport)
35
+ - [Displaying Real-Time Bus Locations](#displaying-real-time-bus-locations)
36
+ - [Clearing Bus Routes](#clearing-bus-routes)
37
+ - [Utility Methods](#utility-methods)
38
+ - [Calculating Bounding Box](#calculating-bounding-box)
39
+ - [Advanced Features](#advanced-features)
40
+ - [Custom Icons and Styling](#custom-icons-and-styling)
41
+ - [Performance Optimization](#performance-optimization)
42
+
43
+ ---
44
+
45
+ ## Getting Started
46
+
47
+ ### Installation
48
+
49
+ First, add the Yarrow Map Web SDK to your project using your preferred package manager.
50
+
51
+ ```bash
52
+ npm install @yarrow-uz/yarrow-map-web-sdk
53
+ ```
54
+
55
+ ### React Usage
56
+
57
+ React APIs are exported from the `@yarrow-uz/yarrow-map-web-sdk/react` subpath.
58
+
59
+ ```tsx
60
+ import { YarrowMapView } from '@yarrow-uz/yarrow-map-web-sdk/react';
61
+
62
+ export const MapScreen = () => {
63
+ return (
64
+ <YarrowMapView
65
+ config={{
66
+ center: [69.2401, 41.2995],
67
+ zoom: 12,
68
+ brandBadgePosition: 'bottom-left',
69
+ }}
70
+ style={{ width: '100%', height: '600px' }}
71
+ />
72
+ );
73
+ };
74
+ ```
75
+
76
+ You can also use the hook for custom composition:
77
+
78
+ ```tsx
79
+ import { useYarrowMap } from '@yarrow-uz/yarrow-map-web-sdk/react';
80
+
81
+ export const MapScreen = () => {
82
+ const { containerRef, isReady, error } = useYarrowMap({
83
+ config: {
84
+ center: [69.2401, 41.2995],
85
+ zoom: 12,
86
+ brandBadgePosition: 'bottom-right',
87
+ },
88
+ });
89
+
90
+ return (
91
+ <div>
92
+ <div ref={containerRef} style={{ width: '100%', height: '600px' }} />
93
+ {isReady && <p>Map ready</p>}
94
+ {error && <p>Failed to initialize map</p>}
95
+ </div>
96
+ );
97
+ };
98
+ ```
99
+
100
+ **SSR note:** The React integration initializes the map only on the client side.
101
+
102
+ ### Initialization
103
+
104
+ To get started, you need to create an instance of `YarrowMap`. This requires a configuration object that specifies the container element, center coordinates, and zoom level.
105
+
106
+ **Important - Coordinate Format**: This SDK uses `[longitude, latitude]` format for map configuration (matching MapLibre GL convention), but `[latitude, longitude]` format for all other methods like routing and markers (matching common usage).
107
+
108
+ ```javascript
109
+ import { YarrowMap } from '@yarrow-uz/yarrow-map-web-sdk';
110
+
111
+ // Configuration for the map
112
+ const mapConfig = {
113
+ container: 'map', // ID of the div element to render the map in
114
+ center: [69.2401, 41.2995], // Initial center [longitude, latitude] - MapLibre convention
115
+ zoom: 12, // Initial zoom level
116
+ };
117
+
118
+ // Create a new map instance
119
+ const yarrowMap = new YarrowMap(mapConfig);
120
+
121
+ // Initialize the map
122
+ yarrowMap.init().then(() => {
123
+ console.log('Map initialized successfully!');
124
+ });
125
+ ```
126
+
127
+ **Note:** By default, all MapLibre controls (zoom, navigation, attribution) are automatically removed during initialization. Use MapLibre's `addControl()` method if you need to add specific controls.
128
+
129
+ ### Configuration Options
130
+
131
+ The `YarrowMapConfig` class accepts a single configuration object:
132
+
133
+ ```javascript
134
+ const mapConfig = new YarrowMapConfig({
135
+ container, // string | HTMLElement
136
+ center, // [number, number] - [lng, lat]
137
+ zoom, // number (default: 10)
138
+ minZoom, // number (default: 0)
139
+ maxZoom, // number (default: 19)
140
+ theme, // 'light' | 'dark' (default: 'light')
141
+ cache, // { enabled?: boolean, lifespanDays?: number }
142
+ brandBadgePosition, // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
143
+ controls // { enabled?: boolean, position?: 'left'|'right', zoom?: boolean, compass?: boolean }
144
+ });
145
+ ```
146
+
147
+ **Example with all options:**
148
+
149
+ ```javascript
150
+ import { YarrowMap, YarrowMapConfig } from '@yarrow-uz/yarrow-map-web-sdk';
151
+
152
+ const mapConfig = new YarrowMapConfig({
153
+ container: 'map',
154
+ center: [69.2401, 41.2995],
155
+ zoom: 12,
156
+ minZoom: 5,
157
+ maxZoom: 18,
158
+ theme: 'dark',
159
+ cache: {
160
+ enabled: true, // Enable local persistent cache (default: false)
161
+ lifespanDays: 30 // Cache lifespan in days (default: 30)
162
+ },
163
+ brandBadgePosition: 'top-right',
164
+ controls: {
165
+ enabled: true, // Yarrow controls are OFF by default
166
+ position: 'right', // Optional side of the map
167
+ zoom: true, // Optional
168
+ compass: true // Optional
169
+ },
170
+ });
171
+
172
+ const yarrowMap = new YarrowMap(mapConfig);
173
+ yarrowMap.init().then(() => {
174
+ console.log('Map initialized successfully!');
175
+ });
176
+ ```
177
+
178
+ ## Basic Map Manipulation
179
+
180
+ ### Changing the Map Style
181
+
182
+ You can change the map's visual style. The available styles are `satellite`, `hybrid`, and the default map style.
183
+
184
+ The map supports up to **85-degree pitch** for 3D perspective views.
185
+
186
+ ```javascript
187
+ // Switch to satellite view
188
+ yarrowMap.changeStyles('satellite');
189
+
190
+ // Switch to hybrid view (satellite with labels)
191
+ yarrowMap.changeStyles('hybrid');
192
+
193
+ // Switch back to the default map style
194
+ yarrowMap.changeStyles();
195
+ ```
196
+
197
+ ### Changing the Map Theme
198
+
199
+ You can set the initial theme in the configuration or switch between light and dark themes dynamically after initialization.
200
+
201
+ **Setting initial theme:**
202
+
203
+ ```javascript
204
+ // Initialize with dark theme
205
+ const mapConfig = new YarrowMapConfig({
206
+ container: 'map',
207
+ center: [69.2401, 41.2995],
208
+ zoom: 12,
209
+ minZoom: 5,
210
+ maxZoom: 18,
211
+ theme: 'dark', // Set initial theme to dark
212
+ });
213
+ ```
214
+
215
+ **Switching theme dynamically:**
216
+
217
+ ```javascript
218
+ // Switch to dark mode
219
+ await yarrowMap.changeTheme('dark');
220
+
221
+ // Switch to light mode (default)
222
+ await yarrowMap.changeTheme('light');
223
+ ```
224
+
225
+ **Features:**
226
+ - Automatically re-fetches appropriate styles from the server
227
+ - Updates all map layers to match the theme
228
+ - Preserves custom icons and markers
229
+ - Seamless theme transition
230
+
231
+ ### Moving the Map View
232
+
233
+ You can programmatically move the map to a new location or fit it to a specific geographic area.
234
+
235
+ **Zooming to a specific point:**
236
+
237
+ ```javascript
238
+ // Fly to a new location with a specific zoom level
239
+ yarrowMap.zoomTo(41.3111, 69.2797, 15); // Latitude, Longitude, Zoom Level
240
+ ```
241
+
242
+ **Fitting the map to a set of features:**
243
+
244
+ This is useful when you want to display a set of points, lines, or polygons and ensure they are all visible.
245
+
246
+ ```javascript
247
+ const geojsonData = {
248
+ type: 'FeatureCollection',
249
+ features: [
250
+ // Your GeoJSON features here
251
+ ],
252
+ };
253
+
254
+ yarrowMap.fitBounds(geojsonData);
255
+ ```
256
+
257
+ ### Changing Background Color
258
+
259
+ You can change the background color of the map programmatically:
260
+
261
+ ```javascript
262
+ // Change the map background to a custom color
263
+ yarrowMap.changeBackgroundColor('#f0f0f0'); // Light gray background
264
+ ```
265
+
266
+ **Note:** This method waits for the map style to be fully loaded before applying the background color change.
267
+
268
+ ## Handling Events
269
+
270
+ You can listen for various user interactions with the map.
271
+
272
+ ### Listen for Map Movement
273
+
274
+ Execute a callback function whenever the map finishes moving.
275
+
276
+ ```javascript
277
+ yarrowMap.onMoveEnd((lat, lng, zoom) => {
278
+ console.log(`Map moved to: ${lat}, ${lng} with zoom: ${zoom}`);
279
+ });
280
+ ```
281
+
282
+ ### Listen for Map Clicks
283
+
284
+ Execute a callback when the user clicks on the map.
285
+
286
+ ```javascript
287
+ yarrowMap.onMapClick((lat, lng) => {
288
+ console.log(`Map clicked at: ${lat}, ${lng}`);
289
+ });
290
+ ```
291
+
292
+ ### Listen for Clicks on POIs or Buildings
293
+
294
+ You can add click listeners to specific groups of features on the map, like points of interest (POIs) or buildings.
295
+
296
+ ```javascript
297
+ // Listen for clicks on POIs
298
+ yarrowMap.onIconClick('pois', (lat, lng, properties) => {
299
+ console.log('POI clicked:', properties);
300
+ });
301
+
302
+ // Listen for clicks on buildings
303
+ yarrowMap.onIconClick('buildings', (lat, lng, properties) => {
304
+ console.log('Building clicked:', properties);
305
+ });
306
+
307
+ // Note: onLayerClick is an alias for onIconClick (they are the same method)
308
+ yarrowMap.onLayerClick('pois', (lat, lng, properties) => {
309
+ console.log('Same as onIconClick');
310
+ });
311
+ ```
312
+
313
+ ## Working with Layers and Data
314
+
315
+ ### Adding a GeoJSON Layer
316
+
317
+ You can add custom data to the map in the form of a GeoJSON layer. This is useful for displaying points, lines, or polygons.
318
+
319
+ ```javascript
320
+ const myData = {
321
+ type: 'FeatureCollection',
322
+ features: [
323
+ {
324
+ type: 'Feature',
325
+ geometry: {
326
+ type: 'Point',
327
+ coordinates: [69.2797, 41.3111],
328
+ },
329
+ properties: {
330
+ name: 'My Point',
331
+ },
332
+ },
333
+ ],
334
+ };
335
+
336
+ yarrowMap.addLayer(
337
+ 'my-custom-layer', // A unique name for the layer
338
+ 'circle', // The type of layer (e.g., 'circle', 'line', 'fill', 'symbol')
339
+ myData,
340
+ {
341
+ 'circle-radius': 10,
342
+ 'circle-color': '#ff0000',
343
+ }
344
+ );
345
+ ```
346
+
347
+ ### Advanced Layer Configuration
348
+
349
+ The `addLayer` method supports advanced configuration options for different layer types:
350
+
351
+ ```javascript
352
+ yarrowMap.addLayer(
353
+ layerName, // string - Unique identifier for the layer
354
+ layerType, // Layer type: 'symbol', 'fill', 'line', 'background', 'raster', 'circle', 'heatmap', 'fill-extrusion', 'hillshade'
355
+ featureCollection, // GeoJSON FeatureCollection
356
+ paint, // Paint properties object
357
+ layout, // Layout properties object
358
+ iconSettings // Icon configuration (width, height)
359
+ );
360
+ ```
361
+
362
+ **Symbol layer with custom icons:**
363
+
364
+ ```javascript
365
+ const symbolData = {
366
+ type: 'FeatureCollection',
367
+ features: [
368
+ {
369
+ type: 'Feature',
370
+ geometry: { type: 'Point', coordinates: [69.2797, 41.3111] },
371
+ properties: { name: 'Custom Point' }
372
+ }
373
+ ]
374
+ };
375
+
376
+ yarrowMap.addLayer(
377
+ 'custom-symbols',
378
+ 'symbol',
379
+ symbolData,
380
+ {
381
+ 'text-halo-color': '#ffffff',
382
+ 'text-halo-width': 2
383
+ },
384
+ {
385
+ 'icon-image': 'custom-icon',
386
+ 'icon-image-data': 'data:image/png;base64,...', // Base64 image data
387
+ 'icon-size': 0.8,
388
+ 'icon-allow-overlap': true,
389
+ 'text-field': ['get', 'name'],
390
+ 'text-offset': [0, 1.5],
391
+ 'text-anchor': 'top',
392
+ 'text-size': 14
393
+ },
394
+ {
395
+ width: 32, // Icon width
396
+ height: 32 // Icon height
397
+ }
398
+ );
399
+ ```
400
+
401
+ **Line layer with styling:**
402
+
403
+ ```javascript
404
+ const lineData = {
405
+ type: 'FeatureCollection',
406
+ features: [
407
+ {
408
+ type: 'Feature',
409
+ geometry: {
410
+ type: 'LineString',
411
+ coordinates: [[69.240, 41.299], [69.280, 41.311]]
412
+ }
413
+ }
414
+ ]
415
+ };
416
+
417
+ yarrowMap.addLayer(
418
+ 'custom-line',
419
+ 'line',
420
+ lineData,
421
+ {
422
+ 'line-color': '#3b82f6',
423
+ 'line-width': 4,
424
+ 'line-opacity': 0.8
425
+ },
426
+ {
427
+ 'line-join': 'round',
428
+ 'line-cap': 'round'
429
+ }
430
+ );
431
+ ```
432
+
433
+ ### Adding and Removing Markers
434
+
435
+ You can add markers to the map to indicate specific locations.
436
+
437
+ ```javascript
438
+ // Add a simple red marker (default color)
439
+ const marker = yarrowMap.addMarker([41.3111, 69.2797]); // [latitude, longitude]
440
+
441
+ // Add a marker with all options
442
+ const customMarker = yarrowMap.addMarker([41.2995, 69.2401], {
443
+ element: customElement, // HTMLElement - Custom marker element
444
+ color: '#0000ff', // string - Marker color (default: '#FF0000')
445
+ draggable: true, // boolean - Allow dragging (default: false)
446
+ anchor: 'bottom', // Anchor position: 'center', 'top', 'bottom', 'left', 'right', 'top-left', 'top-right', 'bottom-left', 'bottom-right'
447
+ onClick: () => { // function - Click event handler
448
+ console.log('Marker clicked!');
449
+ }
450
+ });
451
+
452
+ // Add a marker with custom HTML element
453
+ const customElement = document.createElement('div');
454
+ customElement.innerHTML = '📍';
455
+ customElement.style.fontSize = '24px';
456
+
457
+ const htmlMarker = yarrowMap.addMarker([41.3050, 69.2500], {
458
+ element: customElement,
459
+ anchor: 'center'
460
+ });
461
+
462
+ // Remove a marker
463
+ yarrowMap.removeMarker(marker);
464
+ ```
465
+
466
+ **Marker Options:**
467
+ - `element`: Custom HTML element for the marker
468
+ - `color`: Marker color (default: '#FF0000')
469
+ - `draggable`: Whether the marker can be dragged (default: false)
470
+ - `anchor`: Anchor point of the marker relative to its coordinates
471
+ - `onClick`: Click event handler function
472
+
473
+ ### Removing a Layer
474
+
475
+ You can remove a layer that you've previously added.
476
+
477
+ ```javascript
478
+ yarrowMap.removeLayer('my-custom-layer');
479
+ ```
480
+
481
+ ## Routing
482
+
483
+ The SDK provides powerful routing capabilities.
484
+
485
+ ### Building and Displaying a Simple Route
486
+
487
+ Calculate and display a route between a start and end point.
488
+
489
+ ```javascript
490
+ const start = [41.2995, 69.2401]; // [latitude, longitude]
491
+ const end = [41.3111, 69.2797];
492
+ const profile = 'car'; // 'car', 'bicycle', or 'pedestrian'
493
+
494
+ yarrowMap.buildRouteWithLabels(start, end, profile).then(({ features, directions }) => {
495
+ console.log('Route built:', features);
496
+ console.log('Directions:', directions);
497
+ });
498
+ ```
499
+
500
+ ### Building and Displaying a Multi-Stop Route
501
+
502
+ You can also create a route that passes through multiple waypoints.
503
+
504
+ ```javascript
505
+ const coordinates = [
506
+ [41.2995, 69.2401], // Start [latitude, longitude]
507
+ [41.3111, 69.2797], // Waypoint 1
508
+ [41.325, 69.285], // End
509
+ ];
510
+ const profile = 'car';
511
+ const language = 'en'; // Optional: 'en', 'ru', etc. (default: 'ru')
512
+
513
+ yarrowMap.buildMultiSegmentRouteWithLabels(coordinates, profile, language).then(({ features, directions }) => {
514
+ console.log('Multi-segment route built:', features);
515
+ console.log('Directions:', directions);
516
+ });
517
+ ```
518
+
519
+ **Important Note**: The SDK uses `[latitude, longitude]` format for all user-facing coordinate parameters (routing, markers, etc.), which is then internally converted to MapLibre's `[longitude, latitude]` format where needed.
520
+
521
+ ### Clearing Routes
522
+
523
+ To remove all route-related layers from the map:
524
+
525
+ ```javascript
526
+ yarrowMap.clearAllRoutes();
527
+ ```
528
+
529
+ ### Rendering Multiple Routes
530
+
531
+ You can render multiple route features with automatic color coding:
532
+
533
+ ```javascript
534
+ // Array of route features (GeoJSON LineString features)
535
+ const routes = [
536
+ {
537
+ type: 'Feature',
538
+ geometry: {
539
+ type: 'LineString',
540
+ coordinates: [[69.240, 41.299], [69.280, 41.311], [69.285, 41.315]]
541
+ },
542
+ properties: {
543
+ duration: 15.5,
544
+ distance: 2500
545
+ }
546
+ },
547
+ {
548
+ type: 'Feature',
549
+ geometry: {
550
+ type: 'LineString',
551
+ coordinates: [[69.240, 41.299], [69.275, 41.308], [69.285, 41.315]]
552
+ },
553
+ properties: {
554
+ duration: 18.2,
555
+ distance: 3000
556
+ }
557
+ }
558
+ ];
559
+
560
+ // Render routes with default styling
561
+ yarrowMap.renderRoutes(routes);
562
+
563
+ // Render routes with custom base layer name
564
+ yarrowMap.renderRoutes(routes, 'my-custom-routes');
565
+ ```
566
+
567
+ **Features:**
568
+ - Automatically assigns different colors to each route (#3b82f6, #10b981, #f59e0b, #ef4444, #8b5cf6)
569
+ - Creates separate layers for each route (`{baseLayerName}-0`, `{baseLayerName}-1`, etc.)
570
+ - Default base layer name is 'route' if not specified
571
+
572
+ ## Search
573
+
574
+ ### Highlighting Search Results
575
+
576
+ You can perform a search and display the results on the map. The search is performed around the current map center.
577
+
578
+ ```javascript
579
+ const query = 'Tashkent';
580
+
581
+ const clearHighlights = yarrowMap.highlightSearchResults(query, {
582
+ zoomToResults: true, // Automatically zoom to fit the results
583
+ onResultsUpdate: (results) => {
584
+ console.log('Search results:', results);
585
+ },
586
+ onLoadingStateChange: (state) => {
587
+ // state can be 'firstRender', 'rerender', or false
588
+ console.log('Loading state:', state);
589
+ }
590
+ });
591
+
592
+ // To remove the search results from the map later
593
+ // clearHighlights();
594
+ ```
595
+
596
+ ### Advanced Search Configuration
597
+
598
+ The `highlightSearchResults` method accepts comprehensive configuration options:
599
+
600
+ ```javascript
601
+ const clearFunction = yarrowMap.highlightSearchResults('Tashkent', {
602
+ layerName: 'my-search-layer', // Custom layer name (default: 'search-results')
603
+ iconImage: 'custom-search-icon', // Custom icon image name
604
+ highlightColor: '#ff6b35', // Custom highlight color
605
+ pulseAnimation: true, // Enable pulse animation (default: false)
606
+ zoomToResults: false, // Auto-zoom to results (default: true)
607
+
608
+ // Event handlers
609
+ onIconClick: (lat, lng, properties) => {
610
+ console.log('Search result clicked:', { lat, lng, properties });
611
+ // Custom click behavior
612
+ },
613
+
614
+ onResultsUpdate: (results) => {
615
+ console.log(`Found ${results.length} search results`);
616
+ // Handle results update
617
+ },
618
+
619
+ onLoadingStateChange: (state) => {
620
+ // state can be 'firstRender', 'rerender', or false
621
+ if (state === 'firstRender') {
622
+ console.log('Initial search loading...');
623
+ } else if (state === 'rerender') {
624
+ console.log('Updating search results...');
625
+ } else {
626
+ console.log('Search loading complete');
627
+ }
628
+ }
629
+ });
630
+
631
+ // Clear search results when done
632
+ clearFunction();
633
+ ```
634
+
635
+ **Configuration Options:**
636
+ - `layerName`: Custom name for the search results layer
637
+ - `iconImage`: Custom icon image to use for search results
638
+ - `highlightColor`: Color for highlighting search results
639
+ - `pulseAnimation`: Enable/disable pulse animation effect
640
+ - `zoomToResults`: Automatically zoom to fit search results
641
+ - `onIconClick`: Callback when a search result icon is clicked
642
+ - `onResultsUpdate`: Callback when search results are updated
643
+ - `onLoadingStateChange`: Callback for loading state changes
644
+
645
+ **Features:**
646
+ - Automatic re-searching when the map is moved (debounced)
647
+ - Distance-based filtering (minimum 500m movement triggers re-search)
648
+ - Automatic cleanup of previous search results
649
+ - Real-time loading state tracking
650
+
651
+ ## Public Transport
652
+
653
+ ### Displaying Real-Time Bus Locations
654
+
655
+ You can display the real-time locations of buses on the map.
656
+
657
+ **Show buses for a specific route:**
658
+
659
+ ```javascript
660
+ const routeId = 'some-route-id';
661
+ yarrowMap.showBusRoute(routeId).then(clearBusRoute => {
662
+ // To stop showing the bus route later
663
+ // clearBusRoute();
664
+ });
665
+ ```
666
+
667
+ **Show all buses in the current map view:**
668
+
669
+ If you don't provide a `route_id`, the map will show all buses within the visible area.
670
+
671
+ ```javascript
672
+ yarrowMap.showBusRoute().then(clearBusRoutes => {
673
+ // To stop showing all bus routes
674
+ // clearBusRoutes();
675
+ });
676
+ ```
677
+
678
+ ### Clearing Bus Routes
679
+
680
+ The `showBusRoute` method returns a cleanup function that you can call to stop displaying bus locations and remove all related data:
681
+
682
+ ```javascript
683
+ // Store the cleanup function
684
+ const clearBuses = await yarrowMap.showBusRoute('route-123');
685
+
686
+ // Later, clear the bus route display
687
+ clearBuses();
688
+ ```
689
+
690
+ **What the cleanup function does:**
691
+ - Stops the automatic bus location updates (15-second interval)
692
+ - Removes all bus markers from the map
693
+ - Removes all route layers from the map
694
+ - Removes map move event listeners
695
+ - Clears internal caches (SVG cache, etc.)
696
+
697
+ **Advanced Bus Route Features:**
698
+
699
+ **Smooth Bus Animation:**
700
+ Buses animate smoothly between positions, providing a realistic tracking experience:
701
+ - 16-second animation duration between position updates
702
+ - Uses `requestAnimationFrame` for smooth 60fps interpolation
703
+ - Each bus has independent position tracking (`current` vs `target`)
704
+ - Animation starts automatically when buses are displayed
705
+
706
+ ```javascript
707
+ // Buses will animate smoothly between updates
708
+ const clearBuses = await yarrowMap.showBusRoute('route-123');
709
+ ```
710
+
711
+ **Performance Optimization:**
712
+ - Maximum 100 buses displayed at once (randomly selected if more exist)
713
+ - 5km radius filtering for general bus display (no route_id)
714
+ - Automatic viewport filtering for subsequent updates
715
+ - SVG icon caching for better performance
716
+ - Debounced map movement updates (500ms delay)
717
+
718
+ **Automatic Updates:**
719
+ - Bus locations update every 16 seconds
720
+ - Map movement triggers bus location refresh (for general display)
721
+ - Distance-based filtering (minimum 500m movement)
722
+
723
+ ## Utility Methods
724
+
725
+ ### Calculating Bounding Box
726
+
727
+ The SDK provides a utility method to calculate the bounding box of GeoJSON data:
728
+
729
+ ```javascript
730
+ const geojsonData = {
731
+ type: 'FeatureCollection',
732
+ features: [
733
+ {
734
+ type: 'Feature',
735
+ geometry: {
736
+ type: 'Point',
737
+ coordinates: [69.2797, 41.3111]
738
+ }
739
+ },
740
+ {
741
+ type: 'Feature',
742
+ geometry: {
743
+ type: 'LineString',
744
+ coordinates: [[69.240, 41.299], [69.280, 41.311]]
745
+ }
746
+ }
747
+ ]
748
+ };
749
+
750
+ const boundingBox = yarrowMap.getBoundingBox(geojsonData);
751
+ console.log(boundingBox);
752
+ // Output: { xMin: 69.240, xMax: 69.280, yMin: 41.299, yMax: 41.3111 }
753
+ ```
754
+
755
+ **Supported Geometry Types:**
756
+ - Point
757
+ - LineString
758
+ - Polygon
759
+
760
+ The bounding box contains:
761
+ - `xMin`: Minimum longitude
762
+ - `xMax`: Maximum longitude
763
+ - `yMin`: Minimum latitude
764
+ - `yMax`: Maximum latitude
765
+
766
+ ### Clearing Local Cache
767
+
768
+ You can clear cached map resources programmatically:
769
+
770
+ ```javascript
771
+ await yarrowMap.clearCache();
772
+ ```
773
+
774
+ ## Advanced Features
775
+
776
+ ### Custom Icons and Styling
777
+
778
+ **Loading Custom Icons:**
779
+ The SDK automatically loads and caches icons from the server during initialization. You can reference these icons by name in your layers:
780
+
781
+ ```javascript
782
+ // Icons are automatically available after init()
783
+ yarrowMap.init().then(() => {
784
+ // Use server-provided icons
785
+ yarrowMap.addLayer(
786
+ 'poi-layer',
787
+ 'symbol',
788
+ poiData,
789
+ {},
790
+ {
791
+ 'icon-image': ['get', 'icon_img'], // References the icon from feature properties
792
+ 'icon-size': 0.7
793
+ }
794
+ );
795
+ });
796
+ ```
797
+
798
+ **Custom Icon Data:**
799
+ You can also provide custom icon data directly:
800
+
801
+ ```javascript
802
+ yarrowMap.addLayer(
803
+ 'custom-icons',
804
+ 'symbol',
805
+ symbolData,
806
+ {},
807
+ {
808
+ 'icon-image': 'my-custom-icon',
809
+ 'icon-image-data': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...',
810
+ 'icon-size': 1.0
811
+ },
812
+ {
813
+ width: 48,
814
+ height: 48
815
+ }
816
+ );
817
+ ```
818
+
819
+ ### Performance Optimization
820
+
821
+ **Layer Management:**
822
+ - Layers are automatically cleaned up when re-added with the same name
823
+ - Sources are removed along with layers to prevent memory leaks
824
+ - Icon images are cached to avoid redundant loading
825
+
826
+ **Bus Route Optimization:**
827
+ - Maximum bus limit prevents performance issues with large datasets
828
+ - Viewport-based filtering reduces unnecessary marker creation
829
+ - SVG caching improves rendering performance
830
+ - Debounced updates prevent excessive API calls
831
+
832
+ **Memory Management:**
833
+ - Automatic cleanup of intervals and event listeners
834
+ - Cache clearing on component destruction
835
+ - Proper marker removal prevents memory leaks
836
+
837
+ **Best Practices:**
838
+
839
+ 1. **Always clean up resources:**
840
+ ```javascript
841
+ const clearSearch = yarrowMap.highlightSearchResults('query');
842
+ const clearBuses = await yarrowMap.showBusRoute();
843
+
844
+ // When done, clean up
845
+ clearSearch();
846
+ clearBuses();
847
+ ```
848
+
849
+ 2. **Reuse layer names for dynamic content:**
850
+ ```javascript
851
+ // This will automatically clean up the previous layer
852
+ yarrowMap.addLayer('dynamic-layer', 'circle', newData, paint);
853
+ ```
854
+
855
+ 3. **Use meaningful layer names:**
856
+ ```javascript
857
+ // Good: descriptive and unique
858
+ yarrowMap.addLayer('user-locations-2024', 'symbol', userData);
859
+
860
+ // Avoid: generic names that might conflict
861
+ yarrowMap.addLayer('layer1', 'symbol', userData);
862
+ ```
863
+
864
+ 4. **Handle errors appropriately:**
865
+ ```javascript
866
+ try {
867
+ await yarrowMap.buildRouteWithLabels(start, end, 'car');
868
+ } catch (error) {
869
+ console.error('Route building failed:', error);
870
+ // Handle error appropriately
871
+ }
872
+ ```
873
+
874
+ ---
875
+
876
+ ## API Reference
877
+
878
+ ### YarrowMapConfig Class
879
+
880
+ ```typescript
881
+ constructor(
882
+ config: {
883
+ container: string | HTMLElement;
884
+ center: [number, number];
885
+ zoom?: number; // default: 10
886
+ minZoom?: number; // default: 0
887
+ maxZoom?: number; // default: 19
888
+ theme?: 'light' | 'dark'; // default: 'light'
889
+ cache?: {
890
+ enabled?: boolean; // default: false
891
+ lifespanDays?: number; // default: 30 (1 month)
892
+ };
893
+ brandBadgePosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
894
+ controls?: {
895
+ enabled?: boolean; // default: false
896
+ position?: 'left' | 'right';
897
+ zoom?: boolean; // default: true
898
+ compass?: boolean; // default: true
899
+ };
900
+ }
901
+ )
902
+ ```
903
+
904
+ ### YarrowMap Class Methods
905
+
906
+ | Method | Parameters | Return Type | Description |
907
+ |--------|------------|-------------|-------------|
908
+ | `init()` | None | `Promise<void>` | Initialize the map with styles and icons |
909
+ | `changeStyles(styleType?)` | `styleType?: 'satellite' \| 'hybrid'` | `Promise<any>` | Change map style |
910
+ | `changeTheme(theme)` | `theme: 'light' \| 'dark'` | `Promise<any>` | Switch between light and dark themes |
911
+ | `zoomTo(lat, lng, zoom)` | `lat: number, lng: number, zoom: number` | `void` | Fly to specific coordinates |
912
+ | `fitBounds(data)` | `data: GeoJSON` | `void` | Fit map to show all features |
913
+ | `getBoundingBox(data)` | `data: GeoJSON` | `BoundingBox` | Calculate bounding box of features |
914
+ | `onMoveEnd(callback)` | `callback: (lat, lng, zoom) => void` | `void` | Listen for map movement end |
915
+ | `onMapClick(callback)` | `callback: (lat, lng) => void` | `void` | Listen for map clicks |
916
+ | `onIconClick(layerGroup, callback)` | `layerGroup: 'pois' \| 'buildings', callback: Function` | `void` | Listen for icon clicks |
917
+ | `onLayerClick(layerGroup, callback)` | `layerGroup: 'pois' \| 'buildings', callback: Function` | `void` | Alias for onIconClick (same method) |
918
+ | `changeBackgroundColor(color)` | `color: string` | `void` | Change map background color |
919
+ | `addLayer()` | `layerName, layerType, featureCollection, paint?, layout?, iconSettings?` | `void` | Add a layer to the map |
920
+ | `removeLayer(layerName)` | `layerName: string` | `void` | Remove a layer from the map |
921
+ | `addMarker(coordinates, options?)` | `coordinates: [lat, lng], options?: MarkerOptions` | `Marker \| null` | Add a marker to the map |
922
+ | `removeMarker(marker)` | `marker: Marker` | `void` | Remove a marker from the map |
923
+ | `renderRoutes(routes, baseLayerName?)` | `routes: Feature[], baseLayerName?: string` | `void` | Render multiple route features |
924
+ | `buildRouteWithLabels()` | `start: [lat, lng], end: [lat, lng], profile: string` | `Promise<RouteResult>` | Build and display a route with labels |
925
+ | `buildMultiSegmentRouteWithLabels()` | `coordinates: [lat, lng][], profile: string, language?: string` | `Promise<RouteResult>` | Build multi-segment route |
926
+ | `clearAllRoutes()` | None | `void` | Clear all route layers and popups |
927
+ | `clearCache()` | None | `Promise<void>` | Clear local persistent cache |
928
+ | `highlightSearchResults()` | `query: string, options?: SearchOptions` | `() => void` | Highlight search results with cleanup function |
929
+ | `showBusRoute(routeId?)` | `routeId?: string` | `Promise<() => void>` | Show bus locations with cleanup function |
930
+
931
+ ### Type Definitions
932
+
933
+ ```typescript
934
+ interface BoundingBox {
935
+ xMin: number;
936
+ xMax: number;
937
+ yMin: number;
938
+ yMax: number;
939
+ }
940
+
941
+ interface RouteResult {
942
+ features: any[];
943
+ directions: any[];
944
+ }
945
+
946
+ interface MarkerOptions {
947
+ element?: HTMLElement;
948
+ color?: string;
949
+ draggable?: boolean;
950
+ anchor?: 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
951
+ onClick?: () => void;
952
+ }
953
+
954
+ interface SearchOptions {
955
+ layerName?: string;
956
+ iconImage?: string;
957
+ highlightColor?: string;
958
+ pulseAnimation?: boolean;
959
+ zoomToResults?: boolean;
960
+ onIconClick?: (lat: number, lng: number, properties: any) => void;
961
+ onResultsUpdate?: (results: any[]) => void;
962
+ onLoadingStateChange?: (state: 'firstRender' | 'rerender' | false) => void;
963
+ }
964
+ ```
965
+
966
+ ---
967
+
968
+ ## Version Information
969
+
970
+ - **Current Version**: 1.0.39
971
+ - **Dependencies**: maplibre-gl ^5.5.0, axios ^1.7.9
972
+ - **Repository**: https://git.yarrow.uz/yarrow-sdk/frontend/yarrow-map-web-sdk
973
+
974
+ ## Support
975
+
976
+ For issues, questions, or contributions, please visit the project repository or contact the Yarrow development team.