# Barikoi Documentation
> Barikoi provides developer-friendly tools to integrate maps, search, and location intelligence into applications. Optimized for Bangladesh with comprehensive geospatial data, geocoding services, and mapping solutions.
This file contains all documentation content in a single document following the llmstxt.org standard.
## Map
Barikoi Documentation
# Map
Extends Evented
The Map object represents the map on your page. It exposes methods and properties that enable you to programmatically change the map, and fires events as users interact with it.
You create a Map by specifying a container and other options. Then Bkoi GL JS initializes the map on the page and returns your Map object.
# Parameters
- options Object
- options.container(HTMLElement | string) The HTML element in which Bkoi GL JS will render the map, or the element’s string id. The specified element must have no children.
- options.minZoom number The minimum zoom level of the map (0-24). (optional, default 0)
- options.maxZoom number The maximum zoom level of the map (0-24). (optional, default 22)
- options.minPitch number The minimum pitch level of the map (0-60). (optional, default 0)
- options.maxPitch number The minimum pitch level of the map (0-60). (optional, default 60)
- options.style (Object | string)? The map’s Map style. This must be an a JSON object conforming to the schema described in the Map Style Specification, or a URL to such JSON.To load a style from the Map API, you can use a URL of the form map://styles/:owner/:style, where :owner is your Map account name and :style is the style ID. Or you can use one of the following the predefined Map styles:-
map://styles/map/streets-v11
- map://styles/map/outdoors-v11
- options.container (boolean | string)If true, the map’s position (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page’s URL. For example, http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60. An additional string may optionally be provided to indicate a parameter-styled hash, e.g. http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where foo is a custom parameter and bar is an arbitrary hash distinct from the map hash. (optional, default false)
- options.interactive boolean If false, no mouse, touch, or keyboard listeners will be attached to the map, so it will not respond to interaction. (optional, default true)
- options.bearingSnap number The threshold, measured in degrees, that determines when the map’s bearing will snap to north. For example, with a bearingSnap of 7, if the user rotates the map within 7 degrees of north, the map will automatically snap to exact north. (optional, default 7)
- options.pitchWithRotate boolean If false, the map’s pitch (tilt) control with “drag to rotate” interaction will be disabled. (optional, default true)
- options.clickTolerance number The max number of pixels a user can shift the mouse pointer during a click for it to be considered a valid click (as opposed to a mouse drag). (optional, default 3)
- options.attributionControl boolean If true, an AttributionControl will be added to the map. (optional, default true)
- options.customAttribution string | Array ? String or strings to show in an AttributionControl. Only applicable if options.attributionControl is true.
- options.logoPosition string A string representing the position of the Map wordmark on the map. Valid options are top-left,top-right, bottom-left, bottom-right. (optional, default 'bottom-left')
- options.failIfMajorPerformanceCaveat boolean If true, map creation will fail if the performance of Bkoi GL JS would be dramatically worse than expected (i.e. a software renderer would be used). (optional, default false)
- options.preserveDrawingBuffer boolean If true, the map’s canvas can be exported to a PNG using map.getCanvas().toDataURL(). This is false by default as a performance optimization. (optional, default false)
- options.antialias boolean ? If true, the gl context will be created with MSAA antialiasing, which can be useful for antialiasing custom layers. this is false by default as a performance optimization.
- options.refreshExpiredTiles boolean If false, the map won’t attempt to re-request tiles once they expire per their HTTP cacheControl/expires headers. (optional, default true)
- options.maxBounds LngLatBoundsLike? If set, the map will be constrained to the given bounds.
- options.scrollZoom (boolean | Object ) If true, the “scroll to zoom” interaction is enabled. An Object value is passed as options to ScrollZoomHandler#enable. (optional, default true)
- options.boxZoom boolean If true, the “box zoom” interaction is enabled (see BoxZoomHandler). (optional, default true)
- options.dragRotate boolean If true, the “drag to rotate” interaction is enabled (see DragRotateHandler). (optional, default true)
- options.dragPan (boolean | Object ) If true, the “drag to pan” interaction is enabled. An Object value is passed as options to DragPanHandler#enable. (optional, default true)
- options.keyboard boolean If true, keyboard shortcuts are enabled (see KeyboardHandler). (optional, default true)
- options.doubleClickZoom boolean If true, the “double click to zoom” interaction is enabled (see DoubleClickZoomHandler). (optional, default true)
- options.touchZoomRotate (boolean | Object ) If true, the “pinch to rotate and zoom” interaction is enabled. An Object value is passed as options to TouchZoomRotateHandler#enable. (optional, default true)
- options.touchPitch (boolean | Object ) If true, the “drag to pitch” interaction is enabled. An Object value is passed as options to TouchPitchHandler#enable. (optional, default true)
- options.trackResize boolean If true, the map will automatically resize when the browser window resizes. (optional, default true
- options.center LngLatLike The inital geographical centerpoint of the map. If center is not specified in the constructor options, Bkoi GL JS will look for it in the map’s style object. If it is not specified in the style, either, it will default to [0, 0] Note: Bkoi GL uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON. (optional, default [0, 0]))
- options.zoom number The initial zoom level of the map. If zoom is not specified in the constructor options, Bkoi GL JS will look for it in the map’s style object. If it is not specified in the style, either, it will default to 0. (optional, default 0)
- options.bearing number The initial bearing (rotation) of the map, measured in degrees counter-clockwise from north. If bearing[Link text Here](https://link-url-here.org) is not specified in the constructor options, Bkoi GL JS will look for it in the map’s style object. If it is not specified in the style, either, it will default to 0. (optional, default 0)
- options.pitch number The initial pitch (tilt) of the map, measured in degrees away from the plane of the screen (0-60). If pitch is not specified in the constructor options, Bkoi GL JS will look for it in the map’s style object. If it is not specified in the style, either, it will default to 0. (optional, default 0)
- options.bounds LngLatBoundsLike? The initial bounds of the map. If bounds is specified, it overrides center and zoom constructor options.
- options.fitBoundsOptions Object ? A Map#fitBounds options object to use only when fitting the initial bounds provided above.
- options.renderWorldCopies boolean If true, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to false:- When the map is zoomed out far enough that a single representation of the world does not fill the map’s entire container, there will be blank space beyond 180 and -180 degrees longitude.
- Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the map and the other on the left edge of the map) at every zoom level. (optional, default true)
- options.maxTileCacheSize number The maximum number of tiles stored in the tile cache for a given source. If omitted, the cache will be dynamically sized based on the current viewport. (optional, default null)
- options.localIdeographFontFamily string Defines a CSS font-family for locally overriding generation of glyphs in the ‘CJK Unified Ideographs’, ‘Hiragana’, ‘Katakana’ and ‘Hangul Syllables’ ranges. In these ranges, font settings from the map’s style will be ignored, except for font-weight keywords (light/regular/medium/bold). Set to false, to enable font settings from the map’s style for these glyph ranges. Note that Map Studio sets this value to false by default. The purpose of this option is to avoid bandwidth-intensive glyph server requests. (See Use locally generated ideographs.) (optional, default 'sans-serif')
- options.transformRequest RequestTransformFunction A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests. Expected to return an object with a url property and optionally headers and credentials properties. (optional, default url)
- options.collectResourceTiming boolean If true, Resource Timing API information will be collected for requests made by GeoJSON and Vector Tile web workers (this information is normally inaccessible from the main Javascript thread). Information will be returned in a resourceTiming property of relevant data events. (optional, default false)
- options.fadeDuration number Controls the duration of the fade-in/fade-out animation for label collisions, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading. (optional, default 300)
- options.crossSourceCollisions boolean If true, symbols from multiple sources can collide with each other during collision detection. If false, collision detection is run separately for the symbols in each source. (optional, default true)
- options.locale Object A patch to apply to the default localization table for UI strings, e.g. control tooltips. The locale object maps namespaced UI string IDs to translated strings in the target language; see src/ui/default_locale.js for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table). (optional, default null)
# Example
```js
const map = new bkoigl.Map({
container: "map",
center: [90.3938010872331, 23.821600277500405],
zoom: 12,
style: "https://map.barikoi.com/styles/barikoi-dark/style.json",
maxZoom: 16,
hash: true,
transformRequest: (url, resourceType) => {
if (resourceType === "Source" && url.startsWith("http://myHost")) {
return {
url: url.replace("http", "https"),
headers: { "my-custom-header": true },
credentials: "include", // Include cookies for cross-origin requests
};
}
},
});
```
---
## Autocomplete Tutorial
Barikoi Documentation
# Autocomplete Tutorial
Autocomplete demo using OpenStreetMap.
# Source code:
```html
Barikoi Autocomplete
Barikoi Autocomplete Demo
```
[Get the full tutorial link](https://medium.com/@barikoibd/how-to-add-location-search-autocomplete-functionality-to-your-website-in-2-minutes-using-barikoi-js-f99b351ba997)
---
## Geocoding
Barikoi Documentation
# Geocoding
# Geocode
# Bkoi.geocode(place_id, callback)
This method performs location search using Barikoi Geocode API. It accepts a and a callback function and returns an array containing place information. Place_id can be found in Bkoi.search('example', callback) function's response
# Example
```js
// Get Geo Address
Bkoi.geocode(147, (response) => console.log(JSON.parse(response)));
```
---
## Getting started
Barikoi Documentation
# Getting started
# Get started with barikoi.js
# Overview
A small JavaScript library that provides easy interface to use Barikoi API's
# Authentication
API Key is needed to use the API's. [Sign up](https://developer.barikoi.com/register) in Barikoi Developers Account and get an Account API KEY. Include Barikoi.js in the end of the body tag of your page with your API key:
```html
```
---
## Nearby
Barikoi Documentation
# Nearby
# Bkoi.nearby(long, lat, callback)
This method performs location search using Barikoi Nearby API. It accepts three arguments a longitude, a latitude and a callback function and returns an array of nearby locations
# Example
```js
// Get Nearby Locations
Bkoi.nearby(90.36668110638857, 23.83723803415923, (response) =>
console.log(JSON.parse(response))
);
```
---
## Reverse Geocoding
Barikoi Documentation
# Reverse Geocoding
# Reverse Geocode
# Bkoi.reverseGeo(long, lat, callback)
This method performs location search using Barikoi Reverse Geocode API. It accepts three arguments a longitude, a latitude and a callback function and returns a Place object containing place information
# Example
```js
// Get Reverse Geo Address
Bkoi.reverseGeo(90.36668110638857, 23.83723803415923, (response) =>
console.log(JSON.parse(response))
);
```
---
## Search
Barikoi Documentation
# Search
# Bkoi.search(string, callback)
This method performs location search using Barikoi Search API. It accepts two arguments a query string and a callback function and returns an array of locations
# Example
```js
// Search for 'cafe'
Bkoi.search("cafe", (response) => console.log(response));
```
Barikoi.js provides Autocomplete UI for search. Tutorial on Autocomplete UI can be found [here](https://medium.com/@barikoibd/how-to-add-location-search-autocomplete-functionality-to-your-website-in-2-minutes-using-barikoi-js-f99b351ba997)
---
## Interactive Code Examples
Looking for hands-on examples? We've moved all our interactive code examples to a dedicated **Examples** section for better organization and discoverability.
## [View All Examples →](/examples)
### What You'll Find:
#### **Getting Started**
Perfect for your first Barikoi map integration.
- [Display a Basic Map](/examples/getting-started/display-basic-map)
- [Custom Map Styles](/examples/getting-started/display-custom-map-style)
- [Map Controls](/examples/getting-started/add-maps-control)
#### **Markers & Popups**
Add interactive markers and information windows.
- [Add a Marker](/examples/markers-and-popups/add-marker)
- [Draggable Markers](/examples/markers-and-popups/add-draggable-marker)
- [Click to Add Marker](/examples/markers-and-popups/add-marker-map-click)
- [Popups & Info Windows](/examples/markers-and-popups/add-popup)
- [Multiple Markers](/examples/markers-and-popups/multiple-marker-as-layer)
- [Animated Markers](/examples/markers-and-popups/soft-pulsing-marker)
#### **Layers & Styling**
Work with GeoJSON, lines, polygons, and custom layers.
- [GeoJSON Lines](/examples/layers-and-styling/add-geojson-line)
- [Polygons & Areas](/examples/layers-and-styling/add-polygon)
- [Multiple GeoJSON Layers](/examples/layers-and-styling/multiple-geojson)
- [Custom Icon Layer](/examples/layers-and-styling/icon-layer)
- [Vector Tiles](/examples/layers-and-styling/vector-tile-layer)
#### **Advanced Features**
Real-time tracking, animations, and interactive tools.
- [Live Real-Time Data](/examples/advanced-features/live-real-time-data)
- [Animate Along Path](/examples/advanced-features/animate-point-along-line)
- [Camera Animation](/examples/advanced-features/animate-map-camera)
- [Measure Distance](/examples/advanced-features/measure-distance)
- [Measure Area](/examples/advanced-features/measure-polygon-area)
- [Click to Center](/examples/advanced-features/click-to-center)
- [Fly to Location](/examples/advanced-features/fly-location)
- [Fit Bounds](/examples/advanced-features/fit-bound)
- [Mouse Position](/examples/advanced-features/mouse-position)
---
## 💡 Quick Links
- 📚 [Browse All Examples](/examples) - Interactive examples with live demos
- 🗺️ [Maps API Documentation](/docs/maps-api) - Complete API reference
- 🔑 [Get Your API Key](https://developer.barikoi.com) - Start building today
- 💬 [Get Support](https://barikoi.com/contact) - Need help? Contact us
:::tip Looking for something specific?
Visit the [Examples section](/examples) to see all examples organized by category with live demos and full source code.
:::
---
## Barikoi Android API SDK
[](https://opensource.org/licenses/Apache-2.0)
[](https://android-arsenal.com/api?level=24)
[](https://kotlinlang.org)
[](https://github.com/barikoi/barikoi-api-sdk-android)
A modern, easy-to-use Android SDK for [Barikoi Location APIs](https://docs.barikoi.com/api). Built with Kotlin, Coroutines, and Retrofit.
---
## Features
### 11 Powerful APIs
| API | Description |
|-----|-------------|
| **Reverse Geocoding** | Convert coordinates to human-readable addresses |
| **Autocomplete** | Smart place suggestions as the user types |
| **Rupantor Geocode** | Advanced free-text address formatting and geocoding |
| **Route Overview** | Get distance and duration between two points |
| **Calculate Route** | Turn-by-turn directions via GraphHopper |
| **Route Optimization** | Find the most efficient order for multiple waypoints |
| **Place Search** | Full-text search with session-based place details |
| **Nearby Places** | Find places within a radius |
| **Snap to Road** | Snap a coordinate to the nearest road |
| **Geofencing** | Check whether a point is within a given radius |
### Modern Android Stack
- 100% Kotlin
- Coroutines for async operations
- Retrofit 2.11 + OkHttp 4.12 for networking
- Moshi 1.15 for JSON parsing (KSP code-gen)
- Type-safe sealed-class error handling
### Production Ready
- Automatic API-key injection via OkHttp interceptor
- Locale-safe coordinate formatting (no comma-decimal bugs)
- Comprehensive error mapping (network, HTTP 4xx/5xx, parse, validation)
- ProGuard / R8 consumer rules included
- Five unit-test suites (MockWebServer)
---
## Installation
### Step 1: Add JitPack repository
In your root `settings.gradle.kts`:
```kotlin
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
```
### Step 2: Add the dependency
In your app's `build.gradle.kts`:
```kotlin
dependencies {
implementation("com.github.barikoi:barikoi-api-sdk-android:1.0.0")
}
```
---
## Quick Start
**Step 1 – Initialize once in `Application.onCreate()`:**
```kotlin
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
BarikoiClient.init(
apiKey = BuildConfig.BARIKOI_API_KEY,
enableLogging = BuildConfig.DEBUG
)
}
}
```
> **Note:** Add the **MyApp** class in your `AndroidManifest.xml`
**Step 2 – Use anywhere with no key required:**
```kotlin
class MyActivity : AppCompatActivity() {
// Returns the same cached singleton every time
private val barikoi = BarikoiClient()
fun fetchAddress() {
lifecycleScope.launch {
barikoi.reverseGeocode(23.8103, 90.4125)
.onSuccess { place -> Log.d("Barikoi", "Address: ${place.address}") }
.onFailure { error -> Log.e("Barikoi", "Error: ${error.message}") }
}
}
}
```
For custom timeouts or base URL:
```kotlin
val barikoi = BarikoiClient.Builder()
.apiKey("YOUR_API_KEY")
.enableLogging(BuildConfig.DEBUG)
.connectTimeoutSeconds(60)
.readTimeoutSeconds(60)
.writeTimeoutSeconds(60)
.build()
```
> **Note:** Builder instances are **not** registered as the global singleton.
---
## API Reference
### Reverse Geocoding
Convert coordinates to a human-readable address:
```kotlin
barikoi.reverseGeocode(
latitude = 23.8103,
longitude = 90.4125,
bangla = true // optional: return address in Bangla
).onSuccess { place ->
println("Address : ${place.displayAddress}")
println("Area : ${place.area}")
println("City : ${place.city}")
println("Post : ${place.displayPostCode}")
}.onFailure { error ->
println("Error: ${error.message}")
}
```
### Autocomplete
Get smart place suggestions:
```kotlin
barikoi.autocomplete(
query = "Gulshan",
bangla = true
).onSuccess { places ->
places.forEach { place ->
println("${place.address} – ${place.area}")
}
}
```
### Rupantor Geocode
Format and geocode a free-text address:
```kotlin
barikoi.rupantorGeocode(
query = "House 10, Road 5, Gulshan 1, Dhaka",
thana = true,
district = true,
bangla = false
).onSuccess { response ->
println("Fixed address : ${response.fixedAddress}")
println("Bangla address : ${response.banglaAddress}")
println("Confidence : ${response.confidenceScorePercentage}%")
println("Geocoded latitude : ${response.geocodedAddress?.latitude}")
println("Geocoded longitude : ${response.geocodedAddress?.longitude}")
}
```
### Route Overview
Get distance and duration between two points:
```kotlin
barikoi.routeOverview(
startLat = 23.8103,
startLon = 90.4125,
endLat = 23.7808,
endLon = 90.4217,
profile = "car" // optional: car | foot | bike
).onSuccess { routes ->
routes.firstOrNull()?.let { route ->
println("Distance : ${route.distance} m")
println("Duration : ${route.duration} s")
}
}
```
### Calculate Route
Get detailed turn-by-turn directions via GraphHopper:
```kotlin
barikoi.calculateRoute(
startLat = 23.8103,
startLon = 90.4125,
endLat = 23.7808,
endLon = 90.4217,
profile = "car"
).onSuccess { response ->
response.paths?.firstOrNull()?.let { path ->
println("Distance : ${path.distanceInKm} km")
println("Duration : ${path.durationInMinutes} min")
path.instructions?.forEach { step ->
println(" → ${step.text}")
}
}
}
```
### Route Optimization
Find the most efficient order to visit multiple waypoints:
```kotlin
barikoi.optimizeRoute(
sourceLat = 23.8103,
sourceLon = 90.4125,
destinationLat = 23.7516,
destinationLon = 90.3888,
waypoints = listOf(
Pair(23.7808, 90.4217),
Pair(23.7650, 90.4050)
),
profile = "car"
).onSuccess { response ->
response.paths?.firstOrNull()?.let { path ->
println("Optimized distance : ${path.distanceInKm} km")
println("Optimized duration : ${path.durationInMinutes} min")
}
}
```
### Place Search + Details
Full-text search with session-based place details:
```kotlin
// Step 1 – search
barikoi.searchPlace("Bashundhara").onSuccess { response ->
val sessionId = response.sessionId ?: return@onSuccess
val firstPlace = response.places?.firstOrNull() ?: return@onSuccess
// Step 2 – fetch details with the session ID
barikoi.placeDetails(
placeCode = firstPlace.placeCode ?: return@onSuccess,
sessionId = sessionId
).onSuccess { place ->
println("Name : ${place.name}")
println("Address : ${place.displayAddress}")
println("Lat/Lon : ${place.latitude}, ${place.longitude}")
}
}
```
### Nearby Places
Find places within a radius:
```kotlin
barikoi.nearbyPlaces(
latitude = 23.8103,
longitude = 90.4125,
radius = 1.0, // kilometres
limit = 10
).onSuccess { places ->
places.forEach { place ->
println("${place.address} – ${place.distanceWithinMeters} m away")
}
}
```
### Snap to Road
Snap a coordinate to the nearest road:
```kotlin
barikoi.snapToRoad(
latitude = 23.8103,
longitude = 90.4125
).onSuccess { response ->
println("Snapped to : ${response.snappedLatitude}, ${response.snappedLongitude}")
println("Distance : ${response.distance} m")
}
```
### Geofencing
Check whether a destination is within a radius:
```kotlin
barikoi.checkNearby(
currentLat = 23.8103,
currentLon = 90.4125,
destinationLat = 23.8110,
destinationLon = 90.4130,
radiusMeters = 500.0
).onSuccess { response ->
if (response.isInside) {
println("Inside radius! Distance: ${response.distance} m")
} else {
println("Outside radius. Distance: ${response.distance} m")
}
}
```
---
## Error Handling
All SDK methods return `Result`. Errors are mapped to typed `BarikoiError` subclasses:
```kotlin
barikoi.reverseGeocode(lat, lon).onFailure { error ->
when (error) {
is BarikoiError.NetworkError -> showToast("No internet connection")
is BarikoiError.UnauthorizedError -> showToast("Invalid or missing API key")
is BarikoiError.QuotaExceededError -> showToast("API quota exceeded")
is BarikoiError.RateLimitError -> showToast("Too many requests – try again later")
is BarikoiError.BadRequestError -> showToast("Invalid request parameters")
is BarikoiError.ValidationError -> showToast("Bad input: ${error.message}")
is BarikoiError.ParseError -> showToast("Unexpected response format")
is BarikoiError.HttpError -> showToast("HTTP ${error.code}: ${error.message}")
else -> showToast("Error: ${error.message}")
}
}
```
### Convenience helpers on `BarikoiError`
| Property | Description |
|----------|-------------|
| `isNetworkError` | Connectivity failure (no internet, timeout, DNS) |
| `isAuthError` | HTTP 401 / 403 – invalid or missing API key |
| `isQuotaError` | HTTP 402 – billing / quota limit exceeded |
| `isRateLimitError` | HTTP 429 – too many requests |
| `isValidationError` | Client-side bad input (blank query, invalid coords, etc.) |
| `isParseError` | Response received but could not be parsed |
| `isNotFound` | HTTP 404 |
| `isClientError` | Any 4xx or local validation error |
| `isServerError` | Any 5xx server-side error |
---
## Configuration
### Custom timeouts
```kotlin
val barikoi = BarikoiClient.Builder()
.apiKey("YOUR_API_KEY")
.connectTimeoutSeconds(60)
.readTimeoutSeconds(60)
.writeTimeoutSeconds(60)
.build()
```
### HTTP logging (debug only)
```kotlin
val barikoi = BarikoiClient.Builder()
.apiKey("YOUR_API_KEY")
.enableLogging(true) // prints full request / response bodies in Logcat
.build()
```
---
## Permissions
Add to your `AndroidManifest.xml`:
```xml
```
---
## Requirements
| Item | Version |
|------|---------|
| Min SDK | API 24 (Android 7.0) |
| Target SDK | API 36+ |
| Kotlin | 2.0.21 |
| Coroutines | 1.9.0 |
| Retrofit | 2.11.0 |
| OkHttp | 4.12.0 |
| Moshi | 1.15.1 |
---
## ProGuard / R8
Consumer ProGuard rules are bundled automatically. No extra configuration is needed.
---
## Get Your API Key
1. Sign up at [https://developer.barikoi.com/register](https://developer.barikoi.com/register)
2. Create a new API key from your dashboard
3. Pass it to `BarikoiClient.init()` or `BarikoiClient.Builder().apiKey()`
---
## License
```
Copyright 2025 Barikoi
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
---
## Support
| Channel | Link |
|---------|------|
| Email | hello@barikoi.com |
| Website | https://barikoi.com |
| API Docs | https://docs.barikoi.com/api |
---
*Made by Barikoi*
---
## Get an access token
Barikoi Documentation
If you don't have a Barikoi account, [sign up](https://developer.barikoi.com/register) for one here and then navigate to your Account page. Copy your default public token to your clipboard. After you've added the Barikoi Library as a dependency inside of your Android project, Paste the below code into your application class.
```js
BarikoiAPI.getINSTANCE(getApplicationContext(), "access_token");
```
# If you don't have an application class seperately
If you don't want to have a separate application class for your Project, then paste the same code in every activity class, in which you are using the api's.
---
## Barikoi location library
Barikoi Documentation
# Install the Barikoi Location Library for Android
Before you begin building your app, install the Barikoi Location Library for Android by following our installation guide. The installation guide assumes that you have already downloaded Android Studio and created a new project. You'll make changes to four different files within your project to install the Barikoi Location Library for Android. The four files you'll be working with include: build.gradle (Module:App): Android Studio uses a toolkit called Gradle to compile resources and source code into an APK. This plain text file is used to configure the build and list dependencies, including the Barikoi Location Library for Android. AndroidManifest.xml: This is where you'll describe components of the application. MainActivity.java: This is a Java file where you'll specify Barikoi classes and methods. activity_main.xml: This is where you'll set the properties for your Location related searches, add an fragment to access an activity. Once you've completed all the steps in the installation guide, the four files below should include the following:
# build.gradle (Module:App)
---
## Nearby Library
Barikoi Documentation
Nearby Request Example
---
## Create an Android Studio project
Barikoi Documentation
Familiarize yourself with Android Studio. You'll need to create a new project with an empty activity. Use the following options when creating a new Android Studio project:
- Under Select the form factors your app will run on, check "Phone and Tablet.
- For minimum SDK, select API 16: Android 4.1 (JellyBean). (This is the lowest API level currently supported by Barikoi Location Library for Android.)
- Click Next to advance to the activity selection screen.
- Select Empty Activity and click Next.
- Accept the default Activity Name and Layout Name and click Finish.
---
## Reverse Geocoding Library
Barikoi Documentation
---
## Search Autocomplete Fragment
Barikoi Documentation
To use the Barikoi Search Autocomplete Activity first add the below code snippet in your activity xml file.
# activity_main.xml
# Then in you activity add the below code:
---
## Add polygon
Check out this code to add polygon to the map from polygon points
```kotlin
class PolygonMapActivity : AppCompatActivity() {
private lateinit var map: MapboxMap
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this)
setContentView(R.layout.activity_add_map)
//map style name for style link
val styleId = "osm-liberty"
// get the api key from barikoi developer panel https://developer.barikoi.com
val apiKey = getString(R.string.barikoi_api_key)
// Build the style URL
val styleUrl = "https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey"
//points list for the polygon
val polygonpoints: ArrayList = ArrayList()
polygonpoints.add(LatLng(23.85574361143307, 90.38354443076582))
polygonpoints.add(LatLng(23.823632508626005,90.40521296373265,))
polygonpoints.add(LatLng(23.82639837105691,90.42285014172887))
polygonpoints.add(LatLng(23.86204198543561,90.40050971626783))
// Create map view
mapView= findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
this.map =map
// Set the style after mapView was loaded
map.setStyle(styleUrl){ style->
//after style loaded, then add polygon to map
val polygon = map.addPolygon(PolygonOptions()
.addAll(polygonpoints) //add all points for the polygon
.fillColor(Color.parseColor("#ccebb9")) // you can add fill color and stroke of the polygon from hex string
.strokeColor(Color.parseColor("#080808"))
.alpha(0.3F) // add transparency to the polygon fill color by setting alpha value from 0.0 to 1.0
)
//focus map on the polygon
map.moveCamera( CameraUpdateFactory.newLatLngBounds( LatLngBounds.fromLatLngs(polygon.points), 50))
}
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
```
---
## Add Simple Map
Check out this code to add a simple map to you app
```kotlin
package com.barikoi.mapdemo
class MapActivity : AppCompatActivity() {
private lateinit var map: MapboxMap
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this)
setContentView(R.layout.activity_map)
//map style name for style link
val styleId = "osm-liberty"
// get the api key from barikoi developer panel https://developer.barikoi.com
apiKey = getString(R.string.barikoi_api_key)
// Build the style URL
val styleUrl = "https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey"
// Create map view
mapView= findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
this.map =map
// Set the style after mapView was loaded
map.setStyle(styleUrl)
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
```
---
## Add Geometry
You can add geometry features in map from geojson data using Layers. Check out the below code to add layers in map
```kotlin
class GeometryMapActivity : AppCompatActivity() {
private lateinit var map: MapboxMap
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this)
setContentView(R.layout.activity_geometry_map)
//map style name for style link
val styleId = "osm-liberty"
// get the api key from barikoi developer panel https://developer.barikoi.com
val apiKey = getString(R.string.barikoi_api_key)
// Build the style URL
val styleUrl = "https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey"
//geometry geosjon data
val geometryjson = """
{
"type": "Feature",
"properties": {},
"geometry":
{
"type": "Polygon",
"coordinates": [
[
[90.4245719514072,23.8498893170947],[90.4162572277404,23.8593578587565],
[90.4063700498334,23.8578960563255],[90.406494,23.852892],
[90.400035,23.860683],[90.3924952036647,23.8614377480597],
[90.3852827038556,23.856767705799],[90.393448,23.845689],
[90.3927183589261,23.8419405320778],[90.4014731602744,23.8310647094072],
[90.4070952270173,23.8308912076182],[90.4180521159278,23.8383449571236],
[90.4223406961995,23.8431574279637],[90.421858,23.846121],
[90.4245719514072,23.8498893170947]
]
]
}
}
"""
//create feature object from the Geojson
val feature = Feature.fromJson(geometryjson)
// Create map view
mapView= findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
this.map =map
// Set the style after mapView was loaded
map.setStyle(styleUrl){style->
// Create a GeoJson Source from our feature.
val geojsonSource = GeoJsonSource("dhaka-airport", feature)
// Add the source to the style
style.addSource(geojsonSource)
// Add layer on map using the Geojson Source
val layer = LineLayer("dhaka-airport-line", "dhaka-airport")
.withProperties(
PropertyFactory.lineCap(Property.LINE_CAP_SQUARE),
PropertyFactory.lineJoin(Property.LINE_JOIN_MITER),
PropertyFactory.lineOpacity(1.0f),
PropertyFactory.lineWidth(5f),
PropertyFactory.lineColor("#0094ff")
)
val filllayer = FillLayer("dhaka-airport-fill", "dhaka-airport")
.withProperties(
PropertyFactory.fillColor("#009fff"),
PropertyFactory.fillOpacity(0.5f)
)
// Add the layer at the end
style.addLayer(layer)
style.addLayer(filllayer)
//set camera on the layer
map.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng( 23.845689, 90.393448), 14.0))
}
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
```
---
## Add Line
Check out this code to add lines to your map
```kotlin
class LineMapActivity : AppCompatActivity() {
private lateinit var map: MapboxMap
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this)
setContentView(R.layout.activity_add_map)
//map style name for style link
val styleId = "osm-liberty"
// get the api key from barikoi developer panel https://developer.barikoi.com
val apiKey = getString(R.string.barikoi_api_key)
// Build the style URL
val styleUrl = "https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey"
//Line points array
val linepoints = ArrayList()
// Add line points
linepoints.add(LatLng(23.87397849117633,90.4004025152986,))
linepoints.add(LatLng(23.860512893207584,90.4004025152986))
linepoints.add(LatLng(23.837857327354314,90.41815482211757))
linepoints.add(LatLng(23.82212196615106,90.42035665862238))
linepoints.add(LatLng(23.812428033815493,90.40370527005587))
linepoints.add(LatLng(23.762436156214207,90.39462262442248))
// Create map view
mapView= findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
this.map =map
// Set the style after mapView was loaded
map.setStyle(styleUrl){style->
//after style is set, add line to map
val line :Polyline =map.addPolyline(PolylineOptions()
.addAll(linepoints) //add all the line points
.width(4.0F) //set width of the line
.color(Color.BLUE) // set color of the line
)
//move map camera on the line
map.moveCamera( CameraUpdateFactory.newLatLngBounds( LatLngBounds.fromLatLngs(line.points), 200))
}
//get line click event in map
map.setOnPolylineClickListener(object: OnPolylineClickListener{
override fun onPolylineClick(polyline: Polyline) {
Toast.makeText(applicationContext, "Line clicked", Toast.LENGTH_LONG).show()
}
})
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
```
---
## Add Marker
Check out This code sample for adding a marker to the map
```kotlin
class MarkerMapActivity : AppCompatActivity() {
private lateinit var map: MapboxMap
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this)
setContentView(R.layout.activity_marker_map)
//map style name for style link
val styleId = "osm-liberty"
// get the api key from barikoi developer panel https://developer.barikoi.com
val apiKey = getString(R.string.barikoi_api_key)
// Build the style URL
val styleUrl = "https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey"
// Create map view
mapView= findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
this.map =map
// Set the style after mapView was loaded
map.setStyle(styleUrl){ style->
//add amarker in the map
map.addMarker(
MarkerOptions()
.setPosition(LatLng(23.8345,90.38044)) //set lat lon position of the marker
.setTitle("Map Marker") // set the title of the marker, will show on marker click
)
}
//get marker click event with marker info
map.setOnMarkerClickListener (object: MapboxMap.OnMarkerClickListener{
override fun onMarkerClick(marker: Marker): Boolean {
Toast.makeText(applicationContext, "clicked on marker", Toast.LENGTH_LONG).show()
return true
}
})
}
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
```
---
## QuickStart
To implement Barikoi map in you android app, we recommend using Maplibre Android SDK as it is quick and easy to install and use with Barikoi maps.
### Prerequisite
For this example you will need:
1. A Barikoi API key, which you can get in [Developer Dashboard](https://developer.barikoi.com "Developer Dashboard")
2. Knowledge in Kotlin/java
3. Barikoi map style
### Get Dependencies
Add bintray Maven repositories to your project-level Gradle file (usually `\{project\}/\{app-module\}/build.gradle`).
```kotlin
allprojects {
repositories {
...
mavenCentral()
}
}
```
Then add the dependency in your dependencies `\{ ... \}`:
```kotlin
implementation("org.maplibre.gl:android-sdk:10.2.0")
```
To add the map, add this code in your activity layout xml file
```xml
```
---
## View Current Location
Check out this code to view current location in the map
```kotlin
class LocationMapActivity : AppCompatActivity() {
private lateinit var map: MapboxMap
private lateinit var mapView: MapView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Mapbox.getInstance(this)
setContentView(R.layout.activity_location_map)
//map style name for style link
val styleId = "osm-liberty"
// get the api key from barikoi developer panel https://developer.barikoi.com
val apiKey = getString(R.string.barikoi_api_key)
// Build the style URL
val styleUrl = "https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey"
// Create map view
mapView= findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
this.map =map
// Set the style after mapView was loaded
map.setStyle(styleUrl){style->
//check if location permissions are allowed
if(checkLocationPermission()){
//if location permission allowed, set current location layer on map
setMapcurrentLocationLayer()
}else{
//location permission denied, request permission
requestLocationPermission()
}
}
}
}
//Function to set current location icon layer in map
private fun setMapcurrentLocationLayer(){
//Check if app has location permission , if not, need to add code for permission handling
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
map.let { it ->
val locationComponent = it.locationComponent
val locationComponentOptions =
LocationComponentOptions.builder(this@LocationMapActivity)
.pulseEnabled(true) // adds pulse effect on current location point
.bearingTintColor(Color.RED)
.compassAnimationEnabled(true)
.build()
it.style?.let {
val locationComponentActivationOptions =
buildLocationComponentActivationOptions(it, locationComponentOptions)
locationComponent.activateLocationComponent(locationComponentActivationOptions)
locationComponent.isLocationComponentEnabled = true
locationComponent.cameraMode = CameraMode.TRACKING_GPS
}
}
}
private fun buildLocationComponentActivationOptions(
style: Style,
locationComponentOptions: LocationComponentOptions
): LocationComponentActivationOptions {
return LocationComponentActivationOptions
.builder(this, style)
.locationComponentOptions(locationComponentOptions)
.useDefaultLocationEngine(true)
.locationEngineRequest(
LocationEngineRequest.Builder(750)
.setFastestInterval(750)
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
.build()
)
.build()
}
private fun checkLocationPermission(): Boolean{
return ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
//Function to request location permission
private fun requestLocationPermission(){
val locationPermissionRequest = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
//after permission granted, set current location layer in map
setMapcurrentLocationLayer()
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
// Only approximate location access granted.
setMapcurrentLocationLayer()
} else -> {
// No location access granted.
Toast.makeText(this, "Location permission denied, cannot get nearby places", Toast.LENGTH_LONG).show()
}
}
}
// ...
// Before you perform the actual permission request, check whether your app
// already has the permissions, and whether your app needs to show a permission
// rationale dialog. For more details, see Request permissions.
locationPermissionRequest.launch(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION))
}
override fun onStart() {
super.onStart()
mapView.onStart()
}
override fun onResume() {
super.onResume()
mapView.onResume()
}
override fun onPause() {
super.onPause()
mapView.onPause()
}
override fun onStop() {
super.onStop()
mapView.onStop()
}
override fun onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
}
```
---
## Android Intro
Barikoi Documentation
# First steps with the Barikoi Location Library for Android
The Barikoi Location Library for Android is our location search library for Android. This guide will walk you through installing the Barikoi Location Libraryfor Android with Android Studio, loading a map, changing the map's style, and placing a pin on it.
# Getting started
Here's what you'll need to get started:
- A Barikoi account and access token. Sign up for an account [here](https://developer.barikoi.com/register). You can find your access tokens on your Account page.
- Android Studio. You can download Android Studio for free from Google. In order to install the Barikoi Location Library for Android, you'll need to first download Android Studio and create a project with an empty activity.
- An Android device (physical or virtual). You will need either a physical Android device or an emulated Android device to preview the store finder.
- Google Play Developer Account (optional). If you want to publish your app to Google Play, you'll need a Google Play developer account. Without one, you'll still be able to preview the app on an Android Virtual Device (AVD) or install the app on a physical device.
---
## Autocomplete
Autocomplete - Barikoi APIs
# Autocomplete
Search for places with autocomplete suggestions. Returns matching places with addresses in `English` and `Bangla`, coordinates, and place details. Use for search boxes, address forms, and location pickers.
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.autocomplete({
q: "Dhaka",
bangla: true,
});
const places = result.data?.places || [];
```
## Response
Barikoi Autocomplete returns:
`id`, `longitude`, `latitude`, `address`, `address_bn`, `city`, `city_bn`, `area`, `area_bn`, `postCode`, `pType`, `uCode`
## Optional Parameters
You can customize the autocomplete request by including additional optional parameters:
| Parameter | Type | Description |
| --------- | ------- | -------------------------------------------- |
| `q` | string | **Required.** The search query string |
| `bangla` | boolean | Set to `true` for additional Bangla response |
### Example with Bangla Response
```typescript
const result = await barikoi.autocomplete({
q: "barikoi",
bangla: true,
});
const places = result.data?.places || [];
// Each place includes address_bn, city_bn, area_bn
```
Type Definitions
```typescript
export type AutocompleteParams = {
q: string;
bangla?: boolean;
};
export type AutocompleteSuccess = {
places?: Array<{
id?: number;
longitude?: string | number;
latitude?: string | number;
address?: string;
address_bn?: string;
city?: string;
city_bn?: string;
area?: string;
area_bn?: string;
postCode?: string | number;
pType?: string;
uCode?: string;
}>;
status?: number;
};
```
Check other optional parameters for autocomplete
---
## Calculate Route
Calculate Route - Barikoi APIs
# Calculate Route
Get detailed route information powered by GraphHopper routing engine. Returns comprehensive route data including path coordinates, distance, travel time, turn-by-turn instructions with street names, and elevation data (ascend/descend). Use for GPS navigation apps, route planning, delivery optimization, and mapping applications requiring detailed routing information.
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.calculateRoute({
start: { latitude: 23.8103, longitude: 90.4125 },
destination: { latitude: 23.8, longitude: 90.4 },
type: "gh",
profile: "car",
});
const hints = result.data?.hints;
const paths = result.data?.paths;
```
## Response
This API returns:
`hints`, `info`, `paths` (array with `distance`, `time`, `points`, `instructions`, `ascend`, `descend`)
## Parameters
| Parameter | Type | Description |
| ------------- | ------ | --------------------------------------------------------------------- |
| `start` | object | **Required.** Start coordinates with `latitude` and `longitude` |
| `destination` | object | **Required.** Destination coordinates with `latitude` and `longitude` |
| `type` | string | **Required.** Routing engine type: `"gh"` (GraphHopper) |
| `profile` | string | Routing profile: `"car"`, `"motorcycle"`, or `"bike"` |
### Example with Turn-by-Turn Instructions
```typescript
const result = await barikoi.calculateRoute({
start: { latitude: 23.8103, longitude: 90.4125 },
destination: { latitude: 23.8, longitude: 90.4 },
type: "gh",
profile: "car",
});
const path = result.data?.paths?.[0];
if (path) {
console.log(`Distance: ${(path.distance / 1000).toFixed(2)} km`);
console.log(`Time: ${(path.time / 60000).toFixed(0)} minutes`);
console.log(`Elevation gain: ${path.ascend}m`);
console.log(`Elevation loss: ${path.descend}m`);
// Turn-by-turn instructions
path.instructions?.forEach((instruction, index) => {
console.log(`${index + 1}. ${instruction.text} (${instruction.distance}m)`);
});
}
```
Type Definitions
```typescript
export type CalculateRouteParams = {
start: { latitude: number; longitude: number };
destination: { latitude: number; longitude: number };
type: "gh";
profile?: "car" | "motorcycle" | "bike";
};
export type CalculateRouteSuccess = {
hints?: {
"visited_nodes.sum"?: number;
"visited_nodes.average"?: number;
};
info?: {
copyrights?: Array;
took?: number;
road_data_timestamp?: string;
};
paths?: Array<{
/**
* Distance in meters
*/
distance?: number;
weight?: number;
/**
* Time in milliseconds
*/
time?: number;
transfers?: number;
points_encoded?: boolean;
bbox?: [number, number, number, number];
/**
* GeoJSON LineString with route coordinates
*/
points?: {
type?: "LineString";
coordinates?: Array<[number, number]>;
};
instructions?: Array<{
distance?: number;
heading?: number;
sign?: number;
interval?: [number, number];
text?: string;
time?: number;
street_name?: string;
}>;
legs?: Array;
details?: Array;
ascend?: number;
descend?: number;
snapped_waypoints?: {
type?: "LineString";
coordinates?: Array<[number, number]>;
};
}>;
};
```
---
## Check Nearby
Check Nearby - Barikoi APIs
# Check Nearby
Verify if a location is within a specified radius. Returns "Inside geo fence" or "Outside geo fence" status. Perfect for delivery notifications, driver alerts, and proximity triggers.
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.checkNearby({
current_latitude: 23.8103,
current_longitude: 90.4125,
destination_latitude: 23.8,
destination_longitude: 90.4,
radius: 100,
});
const isInside = result.data?.message === "Inside geo fence";
```
## Response
This API returns:
`message` ("Inside geo fence" or "Outside geo fence"), `status`, `data`
## Parameters
| Parameter | Type | Description |
| ----------------------- | ------ | ------------------------------------------ |
| `current_latitude` | number | **Required.** Current position latitude |
| `current_longitude` | number | **Required.** Current position longitude |
| `destination_latitude` | number | **Required.** Destination/target latitude |
| `destination_longitude` | number | **Required.** Destination/target longitude |
| `radius` | number | **Required.** Radius in meters to check |
### Example for Delivery Notification
```typescript
const result = await barikoi.checkNearby({
current_latitude: 23.8103, // Driver's current location
current_longitude: 90.4125,
destination_latitude: 23.81, // Delivery destination
destination_longitude: 90.413,
radius: 500, // 500 meters radius
});
const isInside = result.data?.message === "Inside geo fence";
if (isInside) {
console.log("Driver is within 500m of delivery location!");
// Trigger notification to customer
} else {
console.log("Driver is still outside the delivery zone.");
}
```
Type Definitions
```typescript
export type CheckNearbyParams = {
destination_latitude: number;
destination_longitude: number;
radius: number;
current_latitude: number;
current_longitude: number;
};
export type CheckNearbySuccess = {
message?: string;
status?: number;
data?: {
id?: string;
name?: string;
radius?: string;
latitude?: string;
longitude?: string;
user_id?: number;
};
};
```
---
## Nearby(Barikoi APIs)
Nearby - Barikoi APIs
# Nearby Places
Find places within a specified radius. Returns nearby locations sorted by distance with names, addresses, and coordinates. Perfect for "nearby stores", POI discovery, restaurant finders, and ATM locators.
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.nearby({
latitude: 23.8103,
longitude: 90.4125,
radius: 1,
limit: 20,
});
const places = result.data?.places || [];
```
## Response
Barikoi Nearby returns:
`id`, `name`, `distance_in_meters`, `longitude`, `latitude`, `pType`, `Address`, `area`, `city`, `postCode`, `subType`, `uCode`
## Parameters
| Parameter | Type | Description |
| ----------- | ------ | ---------------------------------------- |
| `latitude` | number | **Required.** Latitude coordinate |
| `longitude` | number | **Required.** Longitude coordinate |
| `radius` | number | Search radius in kilometers (default: 1) |
| `limit` | number | Maximum number of results to return |
### Example with Custom Radius and Limit
```typescript
const result = await barikoi.nearby({
latitude: 23.810331,
longitude: 90.412521,
radius: 2, // 2 km radius
limit: 10, // Get up to 10 places
});
const places = result.data?.places || [];
places.forEach((place) => {
console.log(`${place.name} - ${place.distance_in_meters}m away`);
});
```
Type Definitions
```typescript
export type NearbyParams = {
longitude: number;
latitude: number;
radius?: number;
limit?: number;
};
export type NearbySuccess = {
places?: Array<{
id?: number;
name?: string;
distance_in_meters?: string | number;
longitude?: string;
latitude?: string;
pType?: string;
Address?: string;
area?: string;
city?: string;
postCode?: string;
subType?: string;
uCode?: string;
}>;
status?: number;
};
```
---
## Installation and Setting Up
Barikoi APIs - TypeScript/JavaScript SDK
# Barikoi APIs - TypeScript/JavaScript SDK
**Barikoi APIs** is the official TypeScript/JavaScript SDK for [Barikoi Location Services](https://barikoi.com). Built on auto-generated code from the official OpenAPI specification, it provides a modern, type-safe interface for accessing a wide range of location-based services including search, geocoding, reverse geocoding, routing, and more.
## Features
- **Auto-generated with @hey-api/openapi-ts** - Always synchronized with OpenAPI specification
- **100% TypeScript Inference** - Zero type imports required, full IntelliSense everywhere
- **Built-in Validation** - Automatic Zod schema validation with clear error messages
- **Runtime API Key Management** - Update API keys dynamically without reinitializing the client
- **Modern Async/await** - Promise-based API with configurable timeouts
- **Custom Error Classes** - ValidationError, BarikoiError, TimeoutError
- **Multiple Build Formats** - ESM, CJS, and TypeScript declarations
## Get Barikoi API Key
To access Barikoi's API services, you need to:
1. Register on [Barikoi Developer Dashboard](https://developer.barikoi.com/register)
2. Verify with your phone number
3. Claim your API key
Once registered, you'll be able to access the full suite of Barikoi API services.
## Installation
Choose the installation method that best fits your project:
```bash
npm install barikoiapis
```
```bash
yarn add barikoiapis
```
```bash
pnpm add barikoiapis
```
```bash
bun add barikoiapis
```
### CDN (For vanilla JavaScript or quick prototyping)
Add the following script tags to your HTML file:
**Using unpkg:**
```html
```
**Using jsDelivr:**
```html
```
## Quick Start
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
// Full TypeScript inference - no type imports needed
const result = await barikoi.autocomplete({ q: "Dhaka" });
const places = result.data?.places || [];
```
## Configuration Options
### API Key Management
```typescript
// Set during initialization
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
// Update API key at runtime
barikoi.setApiKey("new-api-key");
// Get current API key
const currentKey = barikoi.getApiKey();
```
### Timeout Configuration
```typescript
// Set timeout during initialization (in milliseconds)(default: 30000)
const barikoi = createBarikoiClient({
apiKey: "YOUR_KEY",
timeout: 60000, // 60 seconds
});
```
### Custom Base URL
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_KEY",
baseUrl: "https://custom-endpoint.barikoi.xyz",
});
```
---
## Place Details
Place Details - Barikoi APIs
# Place Details
Get detailed place information using a place code and session ID. Returns complete address and coordinates. Use after Search Place to fetch full location data.
:::note
Requires place code and session ID from Search Place request.
:::
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
// First, get the session_id and place_code from Search Place
const searchResult = await barikoi.searchPlace({ q: "barikoi" });
const sessionId = searchResult.data?.session_id;
const placeCode = searchResult.data?.places?.[0]?.place_code;
// Then, get the place details
const details = await barikoi.placeDetails({
place_code: placeCode,
session_id: sessionId,
});
const place = details.data?.place;
```
## Response
This API returns:
`place` (with `address`, `place_code`, `latitude`, `longitude`), `session_id`, `status`
## Parameters
| Parameter | Type | Description |
| ------------ | ------ | -------------------------------------------------- |
| `place_code` | string | **Required.** Place code from Search Place results |
| `session_id` | string | **Required.** Session ID from Search Place results |
### Complete Example with Search Place
```typescript
// Step 1: Search for a place
const searchResult = await barikoi.searchPlace({ q: "barikoi" });
const sessionId = searchResult.data?.session_id;
const places = searchResult.data?.places || [];
if (places.length > 0 && sessionId) {
// Step 2: Get details for the first result
const details = await barikoi.placeDetails({
place_code: places[0].place_code,
session_id: sessionId,
});
const place = details.data?.place;
console.log(`Address: ${place?.address}`);
console.log(`Coordinates: ${place?.latitude}, ${place?.longitude}`);
}
```
Type Definitions
```typescript
export type PlaceDetailsParams = {
place_code: string; // Place code from search place results
session_id: string; // Session ID from search place results
};
export type PlaceDetailsSuccess = {
place?: {
address?: string;
place_code?: string;
latitude?: string;
longitude?: string;
};
session_id?: string;
status?: number;
};
```
---
## Reverse Geocode
Reverse Geocode - Barikoi APIs
# Reverse Geocoding
Convert coordinates to human-readable addresses with administrative details (district, division, thana) in `English` and `Bangla`. Use for displaying user location, delivery addresses, and location tagging.
:::warning
Enabling all optional parameters triggers multiple API calls, consuming more credits. Request only essential parameters.
:::
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.reverseGeocode({
latitude: 23.8103,
longitude: 90.4125,
district: true,
bangla: true,
});
const place = result.data?.place;
```
## Response
This API initially returns:
`id`, `distance_within_meters`, `address`, `area`, `city`, `postCode`
## Optional Parameters
You can customize the reverse geocoding request by including additional optional parameters:
| Parameter | Type | Description |
| --------------- | ------- | ---------------------------------- |
| `latitude` | number | **Required.** Latitude coordinate |
| `longitude` | number | **Required.** Longitude coordinate |
| `district` | boolean | Include district in response |
| `division` | boolean | Include division in response |
| `thana` | boolean | Include thana in response |
| `sub_district` | boolean | Include sub-district in response |
| `union` | boolean | Include union in response |
| `pauroshova` | boolean | Include pauroshova in response |
| `country` | boolean | Include country in response |
| `bangla` | boolean | Include Bangla translations |
| `address` | boolean | Include address details |
| `area` | boolean | Include area details |
| `post_code` | boolean | Include postal code |
| `location_type` | boolean | Include location type |
### Example with District
```typescript
const result = await barikoi.reverseGeocode({
latitude: 23.810331,
longitude: 90.412521,
district: true,
});
const place = result.data?.place;
console.log(place?.district);
```
Type Definitions
```typescript
export type ReverseGeocodeParams = {
longitude: number;
latitude: number;
country_code?: string;
country?: boolean;
district?: boolean;
post_code?: boolean;
sub_district?: boolean;
union?: boolean;
pauroshova?: boolean;
location_type?: boolean;
division?: boolean;
address?: boolean;
area?: boolean;
bangla?: boolean;
thana?: boolean;
};
export type ReverseGeocodeSuccess = {
place?: {
id?: string | number;
distance_within_meters?: number;
address?: string;
area?: string;
city?: string;
postCode?: string;
address_bn?: string;
area_bn?: string;
city_bn?: string;
country?: string;
division?: string;
district?: string;
sub_district?: string;
pauroshova?: string | null;
union?: string | null;
location_type?: string;
address_components?: {
place_name?: string | null;
house?: string | null;
road?: string | null;
} | null;
area_components?: {
area?: string | null;
sub_area?: string | null;
} | null;
thana?: string;
thana_bn?: string;
};
status?: number;
};
```
Check other optional parameters for reverseGeocode
---
## Route Overview
Route Overview - Barikoi APIs
# Route Overview
Get route information between geographical points. Returns route geometry, distance, duration, and waypoints in polyline or GeoJSON format. Use for displaying routes, calculating distances, and showing ETAs.
:::note
Coordinates must be in "longitude,latitude" format.
:::
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.routeOverview({
coordinates: "90.4125,23.8103;90.4000,23.8000",
geometries: "geojson",
});
const route = result.data?.routes?.[0];
const distanceKm = route.distance / 1000;
const durationMin = route.duration / 60;
```
## Response
This API returns:
`code`, `routes` (array with `geometry`, `legs`, `distance`, `duration`, `weight_name`, `weight`), `waypoints`
## Parameters
| Parameter | Type | Description |
| ------------- | ------ | -------------------------------------------------------------------------------- |
| `coordinates` | string | **Required.** Coordinates in format: `longitude,latitude;longitude,latitude;...` |
| `geometries` | string | Response geometry format: `"polyline"`, `"polyline6"`, or `"geojson"` |
| `profile` | string | Routing profile: `"car"` or `"foot"` |
### Example with GeoJSON Geometry
```typescript
const result = await barikoi.routeOverview({
coordinates: "90.4125,23.8103;90.4000,23.8000",
geometries: "geojson",
profile: "car",
});
const route = result.data?.routes?.[0];
const waypoints = result.data?.waypoints || [];
console.log(`Distance: ${(route?.distance / 1000).toFixed(2)} km`);
console.log(`Duration: ${(route?.duration / 60).toFixed(0)} minutes`);
console.log(`Waypoints: ${waypoints.length}`);
```
Type Definitions
```typescript
export type RouteOverviewParams = {
coordinates: string; // Format: longitude,latitude;longitude,latitude;...
geometries?: "polyline" | "polyline6" | "geojson";
profile?: "car" | "foot";
};
export type RouteOverviewSuccess = {
code?: string;
routes?: Array<{
/**
* Encoded polyline (string) or GeoJSON (object)
*/
geometry?:
| string
| {
[key: string]: unknown;
};
legs?: Array<{
steps?: Array;
/**
* Distance in meters
*/
distance?: number;
/**
* Duration in seconds
*/
duration?: number;
summary?: string;
weight?: number;
}>;
distance?: number;
duration?: number;
weight_name?: string;
weight?: number;
}>;
waypoints?: Array<{
hint?: string;
distance?: number;
name?: string | null;
location?: [number, number];
}>;
};
```
---
## Geocode (Rupantor)
Geocode (Rupantor) - Barikoi APIs
# Geocode (Rupantor)
Validate and format addresses with completeness status and confidence score. Returns standardized address format. Use for checkout validation, delivery verification, and CRM data cleaning.
:::note
Uses 2 API calls internally.
:::
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.geocode({
q: "house 23, road 5, mirpur dhaka",
district: "yes",
bangla: "yes",
});
const status = result.data?.address_status;
const confidence = result.data?.confidence_score_percentage;
const fixed = result.data?.fixed_address;
```
## Response
This API returns:
`given_address`, `fixed_address`, `bangla_address`, `address_status`, `geocoded_address`, `confidence_score_percentage`, `status`
## Parameters
| Parameter | Type | Description |
| ---------- | ------------- | -------------------------------------- |
| `q` | string | **Required.** The address query string |
| `thana` | "yes" \| "no" | Include thana in response |
| `district` | "yes" \| "no" | Include district in response |
| `bangla` | "yes" \| "no" | Include Bangla address translation |
### Example with Thana
```typescript
const result = await barikoi.geocode({
q: "barikoihq",
thana: "yes",
});
const geocodedAddress = result.data?.geocoded_address;
console.log(geocodedAddress?.thana);
```
Type Definitions
```typescript
export type GeocodeParams = {
q: string;
thana?: "yes" | "no";
district?: "yes" | "no";
bangla?: "yes" | "no";
};
export type GeocodeSuccess = {
given_address?: string;
fixed_address?: string;
bangla_address?: string;
address_status?: "complete" | "incomplete";
geocoded_address?: {
id?: string | number;
Address?: string;
address?: string;
address_bn?: string;
alternate_address?: string;
area?: string;
area_bn?: string;
bounds?: string | null;
business_name?: string | null;
city?: string;
city_bn?: string;
created_at?: string;
district?: string;
geo_location?: Array;
holding_number?: string | null;
latitude?: string;
location?: string;
location_shape?: string;
longitude?: string;
match_freq?: number;
match_fuzzy?: number;
matching_diff?: number;
new_address?: string;
pType?: string;
place_code?: string;
place_name?: string | null;
popularity_ranking?: number;
postCode?: string | number;
postcode?: string | number;
road_name_number?: string | null;
score?: number;
subType?: string;
sub_area?: string | null;
sub_district?: string;
sub_type?: string;
super_sub_area?: string | null;
thana?: string;
type?: string;
uCode?: string;
union?: string | number | null;
unions?: string | number | null;
updated_at?: string;
user_id?: number;
};
confidence_score_percentage?: number;
status?: number;
};
```
Check other optional parameters for Geocode (Rupantor)
---
## Search Place
Search Place - Barikoi APIs
# Search Place
Search for places and get unique place codes with a session ID. Returns matching places with addresses. Use for business search, landmark lookup, and location selection.
:::note
Each request generates a new session ID required for Place Details API.
:::
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const searchResult = await barikoi.searchPlace({ q: "barikoi" });
const sessionId = searchResult.data?.session_id;
const places = searchResult.data?.places || [];
```
## Response
This API returns:
`places` (array with `address`, `place_code`), `session_id`, `status`
## Parameters
| Parameter | Type | Description |
| --------- | ------ | ------------------------------------- |
| `q` | string | **Required.** The search query string |
### Example
```typescript
const searchResult = await barikoi.searchPlace({ q: "dhaka university" });
const sessionId = searchResult.data?.session_id;
const places = searchResult.data?.places || [];
places.forEach((place) => {
console.log(`${place.address} - Code: ${place.place_code}`);
});
// Use sessionId and place_code for Place Details API
```
Type Definitions
```typescript
export type SearchPlaceParams = {
q: string;
};
export type SearchPlaceSuccess = {
places?: Array<{
address?: string;
place_code?: string;
}>;
session_id?: string;
status?: number;
};
```
---
## Snap to Road
Snap to Road - Barikoi APIs
# Snap to Road
Find the nearest road point to given coordinates. Returns snapped coordinates and distance to road. Use for vehicle tracking, GPS trace alignment, and ride-sharing location accuracy.
## Usage
```typescript
const barikoi = createBarikoiClient({
apiKey: "YOUR_BARIKOI_API_KEY",
});
const result = await barikoi.snapToRoad({
point: "23.8103,90.4125", // Format: latitude,longitude
});
const snapped = result.data?.coordinates;
const distance = result.data?.distance;
```
## Response
This API returns:
`coordinates` (snapped point), `distance` (in meters), `type`
## Parameters
| Parameter | Type | Description |
| --------- | ------ | --------------------------------------------------------- |
| `point` | string | **Required.** Coordinates in format: `latitude,longitude` |
### Example
```typescript
const result = await barikoi.snapToRoad({
point: "23.8103,90.4125",
});
const snappedCoordinates = result.data?.coordinates;
const distanceToRoad = result.data?.distance;
if (snappedCoordinates) {
console.log(`Original: 23.8103, 90.4125`);
console.log(`Snapped: ${snappedCoordinates[1]}, ${snappedCoordinates[0]}`);
console.log(`Distance to nearest road: ${distanceToRoad}m`);
}
```
Type Definitions
```typescript
export type SnapToRoadParams = {
point: string; // Format: latitude,longitude
};
export type SnapToRoadSuccess = {
coordinates?: [number, number];
/**
* Distance in meters
*/
distance?: number;
type?: "Point";
};
```
---
## Example: Add a layer
### Introduction
This example demonstrates how to `add a custom layer` to a map using the `bkoi-gl` library. In this tutorial, we will walk through the steps to initialize a map, load a GeoJSON source, and add a new fill layer that represents the boundary of Dhaka city `(you can customize as needed)`. The layer will be added on top of the map, with the ability to customize its appearance and placement relative to other layers.
### Example
```js
"use client";
const MainMap = () => {
// References to map container and map instance
const mapContainer = useRef(null);
const map = useRef(null);
// State to track if the map has been initialized
const [mapInitialized, setMapInitialized] = useState(false);
useEffect(() => {
// Prevent re-initializing the map if it already exists
if (map.current) return;
// Initialize the map
map.current = new Map({
container: mapContainer.current, // HTML element to contain the map
center: [90.39017821904588, 23.719800220780733], // Initial map center (longitude, latitude)
zoom: 10, // Initial map zoom level
doubleClickZoom: false, // Disable zoom on double-click
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Access token for bkoi-gl
});
// Set mapInitialized to true when the map is initialized
setMapInitialized(true);
// Add Fullscreen control to the map
map.current.addControl(new FullscreenControl());
// Listen for the 'style.load' event to ensure the style is fully loaded
map.current.on("style.load", () => {
// Re-set mapInitialized to true (ensures the style is loaded)
setMapInitialized(true);
// Access the layers in the map style
const layers = map.current.getStyle().layers;
let firstSymbolId;
// Find the first 'symbol' layer in the map style
for (let i = 0; i < layers.length; i++) {
if (layers[i].type === "symbol") {
firstSymbolId = layers[i].id;
break;
}
}
// Add a new GeoJSON source for Dhaka boundary
map.current.addSource("dhaka-boundary", {
type: "geojson",
data: "https://raw.githubusercontent.com/faiazhossain/dhaka-geojson/main/dhaka-geojson.geojson",
});
// Add a new fill layer for Dhaka boundary and insert it below the first symbol layer
map.current.addLayer(
{
id: "dhaka-boundary-fill",
type: "fill", // Layer type: 'fill' for polygons
source: "dhaka-boundary", // Source of GeoJSON data
layout: {},
paint: {
"fill-color": "#f08", // Fill color of the Dhaka boundary
"fill-opacity": 0.4, // Opacity of the fill color
},
},
firstSymbolId // Place the new layer beneath the first symbol layer
);
});
}, []); // Empty dependency array ensures this effect runs only once
// Render the map container with full width and height
return ;
};
// Define styles for the map container
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden", // Ensure the map container does not show scrollbars
};
export default MainMap;
```
---
## Example: Add a linestring layer
### Introduction
In this example, you will learn how to add a `LineString` layer to a map using the `bkoi-gl` library. A LineString represents a series of connected points, which is useful for visualizing routes or paths on a map. The example uses GeoJSON data to define a custom route and displays it as a line on the map with styling options like line width, color, and join types.
### Example
```js
"use client";
const MainMap = () => {
// References to map container and map instance
const mapContainer = useRef(null);
const map = useRef(null);
// State to track if the map has been initialized
const [mapInitialized, setMapInitialized] = useState(false);
useEffect(() => {
// Prevent re-initializing the map if it already exists
if (map.current) return;
// Initialize the map
map.current = new Map({
container: mapContainer.current, // HTML element to contain the map
center: [90.39017821904588, 23.719800220780733], // Initial map center (longitude, latitude)
zoom: 10, // Initial map zoom level
doubleClickZoom: false, // Disable zoom on double-click
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Access token for bkoi-gl
});
// Set mapInitialized to true when the map is initialized
setMapInitialized(true);
// Add Fullscreen control to the map
map.current.addControl(new FullscreenControl());
// Listen for the 'style.load' event to ensure the style is fully loaded
map.current.on("style.load", () => {
// Re-set mapInitialized to true (ensures the style is loaded)
setMapInitialized(true);
// Access the layers in the map style
const layers = map.current.getStyle().layers;
// Add a new GeoJSON source for Dhaka boundary
map.current.addSource("route", {
type: "geojson",
data: {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: [
[90.39592977568441, 23.73819466254021],
[90.3884381455731, 23.739079495318904],
[90.38360476257503, 23.739079504888963],
[90.38215469683195, 23.744388720000913],
[90.37852968658598, 23.750803712875054],
[90.3739378305936, 23.758988095243794],
[90.36716887736895, 23.77314691763415],
[90.35967464842929, 23.77934226128079],
[90.35483773339735, 23.779566575347957],
[90.35096875523459, 23.782001262987862],
[90.35435594458619, 23.786864015173265],
[90.35387598554115, 23.796368929814676],
[90.35339373496487, 23.799020710977217],
[90.35992109156467, 23.8029969084906],
[90.36668705362558, 23.80609505292402],
[90.36910298772528, 23.806098265925556],
[90.37868166683086, 23.78419024025885],
[90.38085730053507, 23.77644734596072],
[90.38279123521215, 23.76671248707426],
[90.38303314260423, 23.75896902301504],
[90.38907534943587, 23.75963375234933],
[90.39004239268218, 23.774455816768167],
[90.39753615372246, 23.778660709098645],
[90.39850260723358, 23.781313843102453],
[90.4258181375858, 23.780872861090273],
],
},
},
});
// Add a new fill layer for Dhaka boundary and insert it below the first symbol layer
map.current.addLayer({
id: "route",
type: "line", // Layer type: 'fill' for polygons
source: "route", // Source of GeoJSON data
layout: {
"line-join": "round",
"line-cap": "round",
},
paint: {
"line-color": "#888",
"line-width": 8,
},
});
});
}, []); // Empty dependency array ensures this effect runs only once
// Render the map container with full width and height
return ;
};
// Define styles for the map container
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden", // Ensure the map container does not show scrollbars
};
export default MainMap;
```
---
## Example: Implementing Style Change in Barikoi GL Maps
### Introduction
This guide demonstrates how to implement and manage multiple map styles using the Barikoi GL package. You'll learn how to define custom styles, initialize the map with those styles, and create a toggle to switch between them.
### Step 1: Define Map Styles
Create an array of styles to be used in your map. Each style contains the style URL, a preview image, and a name.
```js
const styles = [
{
style: `https://map.barikoi.com/styles/osm-bright/style.json?key=${Barikoi_API_key}`,
image: "https://docs.barikoi.com/img/barikoi-light.png",
name: "Light Style",
},
{
style: `https://map.barikoi.com/styles/barkoi_green/style.json?key=${Barikoi_API_key}`,
image: "https://docs.barikoi.com/img/barikoi-green.png",
name: "Green Style",
},
{
style: `https://map.barikoi.com/styles/barikoi-dark-mode/style.json?key=${Barikoi_API_key}`,
image: "https://docs.barikoi.com/img/barikoi-planet.png",
name: "Dark Style",
},
];
```
#### Explanation:
- style: The URL for the style's JSON file, which contains the map's appearance settings.
- image: A thumbnail image to preview the style.
- name: A user-friendly name for the style.
### Step 2: Initialize the Map
Use the useEffect hook to initialize the map. Ensure the map is created only once.
```js
useEffect(() => {
if (map.current) return; // Prevent re-initialization
map.current = new Map({
container: mapContainer.current,
center: [90.39017821904588, 23.719800220780733],
zoom: 10,
doubleClickZoom: false,
styles: styles, // This is where you are passing your desired styles
accessToken: Barikoi_API_key,
});
}, []);
```
### Step 3: Add CSS for Style Drawer
Add custom CSS for the drawer that will allow users to toggle between different map styles. The drawer will display the available styles and their preview images.
```css
/* External CSS for customizing the drawer and toggle button */
/* Style for the drawer container */
.style-drawer {
position: absolute;
bottom: 50px;
right: 10px;
width: 80px;
background-color: #fff;
/* border: 1px solid #ccc; */
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
overflow: hidden;
transition: max-height 0.3s ease;
max-height: 0; /* Initially collapsed */
z-index: 999;
}
/* Style for the toggle button */
.style-drawer-toggle-button {
position: absolute;
right: 10px;
bottom: 20px;
padding: 8px 12px;
height: 40px;
width: 80px;
background-color: #007bff;
color: #fff;
font-size: 14px;
border: none;
border-radius: 8px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
z-index: 1000;
}
/* Style for each style item in the drawer */
.style-item {
display: flex;
align-items: center;
padding: 4px;
cursor: pointer;
border-bottom: 1px solid #eee;
}
/* Style for the thumbnail image */
.style-item-thumbnail {
width: 70px;
height: 30px;
}
/* Style for the style name */
.style-item-name {
font-size: 14px;
color: #333;
}
```
` Notes:`
- This CSS controls the appearance of the style selection drawer and toggle button.
- The drawer will be initially collapsed, expanding when the user clicks the toggle button.
### Full Working Example
Here's the complete example of how to integrate these steps into a React component. This includes the map initialization and the style drawer.
```js
//@ts-nocheck
"use client";
const MainMap = () => {
// Refs for the map container and map instance
const mapContainer = useRef(null);
const map = useRef(null);
const [drawerOpen, setDrawerOpen] = useState(false);
const Barikoi_API_key = "YOUR_BARIKOI_API_KEY";
const styles = [
{
style: `https://map.barikoi.com/styles/barikoi-light/style.json?key=${Barikoi_API_key}`,
image: "https://docs.barikoi.com/img/barikoi-light.svg",
name: "Light Style",
},
{
style: `https://map.barikoi.com/styles/barkoi_green/style.json?key=${Barikoi_API_key}`,
image: "https://docs.barikoi.com/img/barikoi-green.png",
name: "Green Style",
},
{
style: `https://map.barikoi.com/styles/barikoi-dark-mode/style.json?key=${Barikoi_API_key}`,
image: "https://docs.barikoi.com/img/barikoi-dark.png",
name: "Dark Style",
},
];
// Initialize the map only once
useEffect(() => {
if (map.current) return;
map.current = new Map({
container: mapContainer.current,
center: [90.39017821904588, 23.719800220780733],
zoom: 10,
doubleClickZoom: false,
styles: styles, // Pass the styles array here
accessToken: Barikoi_API_key,
});
}, []);
// Toggle the drawer visibility
const toggleDrawer = () => setDrawerOpen(!drawerOpen);
return (
{drawerOpen && (
{styles.map((style, index) => (
{style.name}
))}
)}
);
};
// Styles for the map container
const containerStyles = {
width: "100%",
height: "98vh",
minHeight: "400px",
overflow: "hidden",
};
export default MainMap;
```
```css
/* Style for the drawer container */
.style-drawer {
position: absolute;
bottom: 50px;
right: 10px;
width: 80px;
background-color: #fff;
/* border: 1px solid #ccc; */
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
overflow: hidden;
transition: max-height 0.3s ease;
max-height: 0; /* Initially collapsed */
z-index: 999;
}
/* Style for the toggle button */
.style-drawer-toggle-button {
position: absolute;
right: 10px;
bottom: 20px;
padding: 8px 12px;
height: 40px;
width: 80px;
background-color: #007bff;
color: #fff;
font-size: 14px;
border: none;
border-radius: 8px;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
z-index: 1000;
}
/* Style for each style item in the drawer */
.style-item {
display: flex;
align-items: center;
padding: 4px;
cursor: pointer;
border-bottom: 1px solid #eee;
}
/* Style for the thumbnail image */
.style-item-thumbnail {
width: 70px;
height: 30px;
}
/* Style for the style name */
.style-item-name {
font-size: 14px;
color: #333;
}
```
### How it will look after implementation

---
## Example: Using Popup with Barikoi GL
### Introduction
This guide demonstrates how to add a `popup` to a map using the `bkoi-gl` package in a React component. Popups allow you to display information about a specific location on the map when it is clicked or hovered over.
### Example
```js
"use client";
const MainMap = () => {
// Reference to the map container HTML element
const mapContainer = useRef(null);
// Reference to the map instance
const map = useRef(null);
useEffect(() => {
// Initialize the map only if it has not been initialized yet
if (map.current) return;
// Create a new map instance and assign it to the map reference
map.current = new Map({
container: mapContainer.current, // The HTML element that will contain the map
center: [90.39017821904588, 23.719800220780733], // Initial center of the map ([longitude, latitude])
zoom: 10, // Initial zoom level of the map
doubleClickZoom: false, // Disable zoom on double-click
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Access token for the bkoi-gl service
});
// Create a new popup and set its position and content
const popup = new Popup({ closeOnClick: false })
.setLngLat([90.36402, 23.82373]) // Position of the popup ([longitude, latitude])
.setHTML("Barikoi!") // Content of the popup
.addTo(map.current); // Add the popup to the map instance
}, []); // Empty dependency array means this effect runs once on component mount
// Render the map container with full width and height
return ;
};
// Define styles for the map container to fill the viewport
const containerStyles = {
width: "100%", // Full width of the container
height: "100vh", // Full viewport height
minHeight: "400px", // Minimum height of 400px
overflow: "hidden", // Hide scrollbars
};
export default MainMap;
```
---
## Example: Using Barikoi GL in React or Next.js
### Introduction
This guide demonstrates how to integrate the `bkoi-gl` package into a React or Next.js component to display a map. It will show how to initialize a map with basic settings such as the center location, zoom level, and more. The example provided is simple yet powerful, allowing you to quickly embed interactive maps into your projects.
### Example
```js
"use client";
const MainMap = () => {
// Refs for the map container and map instance
const mapContainer = useRef(null);
const map = useRef(null);
useEffect(() => {
if (map.current) return; // Ensures map initializes only once
map.current = new Map({
container: mapContainer.current,
center: [90.39017821904588, 23.719800220780733], // Coordinates for Dhaka, Bangladesh
zoom: 10,
doubleClickZoom: false,
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Replace with your Barikoi API key
});
}, []);
return ;
};
// Styles for the map container
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default MainMap;
```
---
## Example: Fly to a certain point
### Introduction
This guide demonstrates how to use the `flyTo` function with the `bkoi-gl` package to animate the map to a specific location when a button is clicked.
### Example
```js
"use client";
const MainMap = () => {
// Refs for map container and map instance
const mapContainer = useRef(null);
const map = useRef(null);
// State to manage the button click
const [mapInitialized, setMapInitialized] = useState(false);
useEffect(() => {
if (map.current) return; // Ensures map initializes only once
// Initialize the map
map.current = new Map({
container: mapContainer.current,
center: [90.39017821904588, 23.719800220780733], // Dhaka coordinates
zoom: 10,
doubleClickZoom: false,
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Replace with your Barikoi API key
});
// Set mapInitialized to true when the map is ready
setMapInitialized(true);
}, []);
// Function to handle flyTo on button click
const handleFlyTo = () => {
if (map.current) {
map.current.flyTo({
center: [90.4, 23.8], // New coordinates to fly to
zoom: 12, // New zoom level
speed: 1, // Speed of the animation (0 to 1)
curve: 1, // The curve of the flyTo animation (0 to 1)
easing: (t) => t, // Easing function for the animation
});
}
};
return (
{mapInitialized && (
)}
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
const buttonStyles = {
position: "absolute",
top: "10px",
left: "10px",
padding: "10px 20px",
backgroundColor: "#007bff",
color: "#fff",
border: "none",
borderRadius: "5px",
cursor: "pointer",
zIndex: 1, // Ensure the button is above the map
};
export default MainMap;
```
---
## Barikoi GL JS
[](https://www.npmjs.com/package/bkoi-gl)
[](https://www.npmjs.com/package/bkoi-gl)
[](https://bundlephobia.com/package/bkoi-gl)
[](https://www.typescriptlang.org/)
[](https://nodejs.org/)
[](https://opensource.org/licenses/MIT)
---
## Overview
Barikoi GL JS is a JavaScript library built on top of [MapLibre GL JS](https://maplibre.org/maplibre-gl-js/docs/), designed for seamless integration with Barikoi Maps, offering high-performance and customizable map rendering. This library is optimized for modern web applications and supports React, Next.js, and vanilla JavaScript projects.
Powered by [Barikoi - Maps for Businesses](https://barikoi.com/), this package provides tools to integrate maps and location services effortlessly.
> **Info:** For React/Next.js integrations, we recommend our react-bkoi-gl npm library.
---
## Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Configuration](#configuration)
- [Map Options](#map-options)
- [Draw Options](#draw-options)
- [Map Events](#map-events)
- [Event Listener Management](#event-listener-management)
- [Markers & Popups](#markers--popups)
- [Map Controls](#map-controls)
- [Camera Methods](#camera-methods)
- [Custom Layers & Sources](#custom-layers--sources)
- [Utility Methods](#utility-methods)
---
## Features
- **High Performance** - WebGL-based map rendering
- **Framework Support** - Easy integration with React and Next.js
- **Customizable Controls** - Flexible map controls and interactions
- **Location Services** - Support for Barikoi geolocation services
- **Lightweight** - Optimized for production use
- **Drawing Tools** - Built-in polygon, line, and point drawing
- **Minimap Control** - Synchronized overview map with customizable styling
- **Branded Attribution** - Displays Barikoi logo with proper attribution
- **Multiple Build Formats** - ESM, CJS, IIFE, and UMD
- **TypeScript Support** - Full TypeScript definitions included
---
## Get Barikoi API Key
To access Barikoi's API services, you need to:
1. Register on [Barikoi Developer Dashboard](https://developer.barikoi.com/register)
2. Verify with your phone number
3. Claim your API key
Once registered, you'll be able to access the full suite of Barikoi API services. If you exceed the free usage limits, you'll need to subscribe to a paid plan.
---
## Installation
Choose the installation method that best fits your project:
### Option 1: CDN (For vanilla JavaScript or quick prototyping)
Add the following links to the `` section of your HTML file:
**Using unpkg:**
```html
```
**Using jsDelivr:**
```html
```
### Option 2: Package Manager (Recommended for React, Next.js, or bundler-based projects)
Install the package using npm:
```bash
npm install bkoi-gl
```
Or using yarn:
```bash
yarn add bkoi-gl
```
Then import the library in your JavaScript/TypeScript files:
```javascript
```
> **Info:** You can also use the full path `"bkoi-gl/dist/style/bkoi-gl.css"` for backward compatibility.
---
## Quick Start
### Vanilla JavaScript
```html
```
### React/Next.js
```typescript title="components/BasicMap.tsx"
"use client";
const BasicMap = () => {
const mapContainer = useRef(null);
const map = useRef(null);
useEffect(() => {
if (map.current) return;
if (!mapContainer.current) return;
map.current = new Map({
container: mapContainer.current,
accessToken: "YOUR_BARIKOI_API_KEY_HERE",
center: [90.39017821904588, 23.719800220780733], // Dhaka coordinates
zoom: 10,
polygon: true, // Enable draw polygon option
drawOptions: {
controls: {
polygon: true,
trash: true
}
}
});
// Cleanup on unmount
return () => {
map.current?.remove();
map.current = null;
};
}, []);
return (
);
};
export default BasicMap;
```
---
## Configuration
### Map Options
The `Map` constructor accepts an options object extending MapLibre GL JS MapOptions with Barikoi-specific additions.
| Option | Type | Default | Description |
| --------------------- | -------------------------------- | -------------------- | ----------------------------------------------------------- |
| `container` | string \| HTMLElement | _required_ | The HTML element or ID to render the map in |
| `accessToken` | string | _required_ | Your Barikoi API key for authentication |
| `style` | string | Barikoi Light | Map style URL or style identifier |
| `center` | [number, number] | `[90.3938, 23.8216]` | Initial center position [longitude, latitude] |
| `zoom` | number | `10` | Initial zoom level (0-22) |
| `bearing` | number | `0` | Initial bearing (rotation) in degrees, clockwise from north |
| `pitch` | number | `0` | Initial pitch (tilt) in degrees (0-85) |
| `minZoom` | number | `0` | Minimum zoom level |
| `maxZoom` | number | `22` | Maximum zoom level |
| `minPitch` | number | `0` | Minimum pitch level |
| `maxPitch` | number | `85` | Maximum pitch level |
| `bounds` | [number, number, number, number] | _none_ | Initial map bounds as [swLng, swLat, neLng, neLat] |
| `fitBoundsOptions` | object | _none_ | Options for fitBounds animation |
| `interactive` | boolean | `true` | Enable/disable map interactions (drag, zoom, rotate) |
| `pitchWithRotate` | boolean | `true` | Enable pitch with rotate gesture |
| `clickTolerance` | number | `3` | Max pixels between mouse down/up for click |
| `scrollZoom` | boolean \| object | `true` | Enable/disable scroll zoom |
| `boxZoom` | boolean | `true` | Enable/disable box zoom |
| `dragRotate` | boolean | `true` | Enable/disable drag to rotate |
| `dragPan` | boolean | `true` | Enable/disable drag to pan |
| `keyboard` | boolean | `true` | Enable/disable keyboard controls |
| `doubleClickZoom` | boolean | `true` | Enable/disable double-click zoom |
| `touchZoomRotate` | boolean \| object | `true` | Enable/disable touch zoom/rotate |
| `touchPitch` | boolean \| object | `true` | Enable/disable touch pitch |
| `antialias` | boolean | _auto_ | Enable antialiasing |
| `refreshExpiredTiles` | boolean | `true` | Refresh expired tiles |
| `maxBounds` | [number, number, number, number] | _none_ | Constrain map to bounds [swLng, swLat, neLng, neLat] |
| `projection` | string | `'mercator'` | Map projection ('mercator' or 'globe') |
| `renderWorldCopies` | boolean | `true` | Render multiple copies of the world |
| `locale` | object | _none_ | Localization strings for UI |
| `polygon` | boolean | `false` | Enable drawing tools for polygons/lines/points |
| `drawOptions` | object | `{}` | Configuration for drawing tools |
| `minimap` | object | _none_ | Configuration for minimap control |
---
### Draw Options
When `polygon: true` is set, the drawing tools are enabled. The `drawOptions` configures the available drawing controls.
#### Available Drawing Modes
| Mode | Description |
| ------------------ | ------------------------------------------------ |
| `simple_select` | Default mode. Click to select features (default) |
| `direct_select` | Select and edit vertices of a feature |
| `draw_polygon` | Draw a polygon by clicking points |
| `draw_line_string` | Draw a line by clicking points |
| `draw_point` | Place a point marker by clicking |
#### Basic Configuration
```javascript
drawOptions: {
// Controls which drawing tools are available
displayControlsDefault: true, // Show all controls by default
controls: {
polygon: true, // Enable polygon drawing tool
line_string: true, // Enable line drawing tool
point: true, // Enable point marker tool
trash: true // Enable delete button
},
// Set the initial mode
defaultMode: 'simple_select', // Options: 'simple_select', 'direct_select', 'draw_polygon', 'draw_line_string', 'draw_point'
// Enable custom properties on features
userProperties: true
}
```
Custom Styles Configuration (Advanced)
You can customize the appearance of drawn features using the `styles` array:
```javascript
drawOptions: {
displayControlsDefault: true,
controls: {
polygon: true,
line_string: true,
point: true,
trash: true
},
defaultMode: 'simple_select',
userProperties: true,
// Custom styles for drawn features
styles: [
// POLYGON STYLES
// Polygon fill (when being drawn or selected)
{
id: 'gl-draw-polygon-fill',
type: 'fill',
filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']],
paint: {
'fill-color': '#D20C0C',
'fill-opacity': 0.3
}
},
// Polygon outline (when being drawn or selected)
{
id: 'gl-draw-polygon-stroke-active',
type: 'line',
filter: ['all', ['==', '$type', 'Polygon'], ['!=', 'mode', 'static']],
layout: { 'line-cap': 'round', 'line-join': 'round' },
paint: {
'line-color': '#D20C0C',
'line-width': 2
}
},
// Polygon outline (static/completed)
{
id: 'gl-draw-polygon-stroke-static',
type: 'line',
filter: ['all', ['==', '$type', 'Polygon'], ['==', 'mode', 'static']],
layout: { 'line-cap': 'round', 'line-join': 'round' },
paint: {
'line-color': '#D20C0C',
'line-width': 2
}
},
// LINE STRING STYLES
// Line (when being drawn or selected)
{
id: 'gl-draw-line-active',
type: 'line',
filter: ['all', ['==', '$type', 'LineString'], ['!=', 'mode', 'static']],
layout: { 'line-cap': 'round', 'line-join': 'round' },
paint: {
'line-color': '#D20C0C',
'line-width': 3
}
},
// Line (static/completed)
{
id: 'gl-draw-line-static',
type: 'line',
filter: ['all', ['==', '$type', 'LineString'], ['==', 'mode', 'static']],
layout: { 'line-cap': 'round', 'line-join': 'round' },
paint: {
'line-color': '#D20C0C',
'line-width': 2
}
},
// POINT STYLES
// Point marker (when being placed or selected)
{
id: 'gl-draw-point-active',
type: 'circle',
filter: ['all', ['==', '$type', 'Point'], ['!=', 'mode', 'static']],
paint: {
'circle-radius': 8,
'circle-color': '#D20C0C',
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
},
// Point marker (static/completed)
{
id: 'gl-draw-point-static',
type: 'circle',
filter: ['all', ['==', '$type', 'Point'], ['==', 'mode', 'static']],
paint: {
'circle-radius': 6,
'circle-color': '#D20C0C',
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
},
// VERTEX & MIDPOINT STYLES (for editing)
// Vertex points (corners when editing polygon/line)
{
id: 'gl-draw-vertex',
type: 'circle',
filter: ['all', ['==', 'meta', 'vertex'], ['==', '$type', 'Point']],
paint: {
'circle-radius': 6,
'circle-color': '#ffffff',
'circle-stroke-width': 2,
'circle-stroke-color': '#D20C0C'
}
},
// Midpoint points (for adding new vertices)
{
id: 'gl-draw-midpoint',
type: 'circle',
filter: ['all', ['==', 'meta', 'midpoint'], ['==', '$type', 'Point']],
paint: {
'circle-radius': 4,
'circle-color': '#D20C0C',
'circle-stroke-width': 1,
'circle-stroke-color': '#ffffff'
}
}
]
}
```
Style Filter Reference
| Filter | Description |
| ------------------------------- | ------------------------------------ |
| `['==', '$type', 'Polygon']` | Matches polygon features |
| `['==', '$type', 'LineString']` | Matches line features |
| `['==', '$type', 'Point']` | Matches point features |
| `['!=', 'mode', 'static']` | Feature is being drawn or selected |
| `['==', 'mode', 'static']` | Feature is completed/static |
| `['==', 'meta', 'vertex']` | Vertex points for editing |
| `['==', 'meta', 'midpoint']` | Midpoint markers for adding vertices |
---
## Map Events
Events are categorized by their purpose for easier navigation.
### Map Lifecycle Events
Fired during the map's initialization and rendering cycle.
| Event | Description | Event Data |
| -------- | ---------------------------------------------------------------- | ---------------------- |
| `load` | Fired when the map has finished loading all resources | - |
| `render` | Fired after the map completes a render cycle | - |
| `idle` | Fired when the map enters an idle state (no ongoing transitions) | - |
| `error` | Fired when an error occurs | `error` - Error object |
**Example:**
```javascript
map.on('load', () => {
console.log('Map is ready for interaction')
})
map.on('error', e => {
console.error('Map error:', e.error)
})
```
### Camera Movement Events
Fired when the map's camera position changes (pan, zoom, rotate, pitch).
| Event | Description | Event Data |
| ------------- | --------------------------------------- | ---------- |
| `movestart` | Fired when camera movement begins | - |
| `move` | Fired repeatedly during camera movement | - |
| `moveend` | Fired when camera movement ends | - |
| `zoomstart` | Fired when zoom level begins changing | - |
| `zoom` | Fired repeatedly during zoom | - |
| `zoomend` | Fired when zoom level change ends | - |
| `rotate` | Fired during rotation (bearing change) | - |
| `rotatestart` | Fired when rotation begins | - |
| `rotateend` | Fired when rotation ends | - |
| `pitch` | Fired during pitch (tilt) change | - |
**Example:**
```javascript
map.on('moveend', () => {
const center = map.getCenter()
const zoom = map.getZoom()
console.log(`Map moved to [${center.lng}, ${center.lat}] at zoom ${zoom}`)
})
map.on('zoom', () => {
console.log('Current zoom level:', map.getZoom())
})
```
### Mouse & Pointer Events
Fired when users interact with the map using mouse or touch input.
| Event | Description | Event Data |
| ----------- | ------------------------------------ | ----------------- |
| `click` | Fired when the map is clicked | `lngLat`, `point` |
| `dblclick` | Fired when the map is double-clicked | `lngLat`, `point` |
| `mousedown` | Fired when mouse button is pressed | `lngLat`, `point` |
| `mouseup` | Fired when mouse button is released | `lngLat`, `point` |
| `mousemove` | Fired when mouse moves over the map | `lngLat`, `point` |
| `mouseover` | Fired when mouse enters the map | `lngLat`, `point` |
| `mouseout` | Fired when mouse leaves the map | `lngLat`, `point` |
| `wheel` | Fired when mouse wheel is used | - |
**Example:**
```javascript
map.on('click', e => {
console.log(`Clicked at [${e.lngLat.lng}, ${e.lngLat.lat}]`)
// Place a marker or show popup at e.lngLat
})
map.on('mousemove', e => {
console.log('Mouse position:', e.lngLat)
})
```
### Drawing Events
> **Info:** Drawing events require `polygon: true` to be set in your Map configuration.
Available when drawing tools are enabled via `polygon: true`.
| Event | Description | Event Data |
| ---------------------- | ----------------------------------- | -------------------------------------- |
| `draw.create` | Fired when a feature is created | `features` - Array of created features |
| `draw.update` | Fired when a feature is updated | `features` - Array of updated features |
| `draw.delete` | Fired when a feature is deleted | `features` - Array of deleted features |
| `draw.selectionchange` | Fired when selection changes | `features`, `points` |
| `draw.modechange` | Fired when draw mode changes | `mode` - Current mode name |
| `draw.actionable` | Fired when available actions change | `actionable` - Action state object |
**Example:**
```javascript
// Handle feature creation
map.on('draw.create', e => {
const feature = e.features[0]
const geometryType = feature.geometry.type
switch (geometryType) {
case 'Polygon':
console.log('Polygon created:', feature.geometry.coordinates)
break
case 'LineString':
console.log('Line created:', feature.geometry.coordinates)
break
case 'Point':
console.log('Point created:', feature.geometry.coordinates)
break
}
})
// Handle feature updates
map.on('draw.update', e => {
console.log('Feature updated:', e.features)
})
// Handle feature deletion
map.on('draw.delete', e => {
console.log('Feature deleted:', e.features)
})
```
---
### Event Listener Management
#### Adding Event Listeners
Use `map.on()` to attach event listeners:
```javascript
// Anonymous function
map.on('load', () => {
console.log('Map loaded')
})
// Named function (easier to remove later)
function handleLoad() {
console.log('Map loaded')
}
map.on('load', handleLoad)
// Once - listener fires only once
map.once('load', () => {
console.log('This will only fire once')
})
```
#### Removing Event Listeners
Use `map.off()` to remove event listeners:
```javascript
// Remove specific listener
map.off('load', handleLoad)
// Remove all listeners for an event
map.off('load')
// Remove all listeners
map.off()
```
#### Getting Event Data
Event handlers receive an event object with contextual data:
```javascript
map.on('click', e => {
// Geographic coordinates
console.log('Lng:', e.lngLat.lng)
console.log('Lat:', e.lngLat.lat)
// Pixel coordinates
console.log('X:', e.point.x)
console.log('Y:', e.point.y)
// Original event
console.log('Original event:', e.originalEvent)
})
```
---
## Markers & Popups
### Adding Markers
Markers are visual indicators placed at specific locations on the map.
```javascript
// Basic marker
const marker = new bkoigl.Marker().setLngLat([90.39, 23.82]).addTo(map)
// Marker with custom color
const marker = new bkoigl.Marker({ color: '#ff0000' }).setLngLat([90.39, 23.82]).addTo(map)
// Marker with custom element
const el = document.createElement('div')
el.className = 'custom-marker'
el.style.backgroundImage = 'url(marker.png)'
el.style.width = '30px'
el.style.height = '30px'
const marker = new bkoigl.Marker(el).setLngLat([90.39, 23.82]).addTo(map)
// Remove a marker
marker.remove()
```
### Adding Popups
Popups display information when clicked or hovered.
```javascript
// Basic popup
const popup = new bkoigl.Popup()
.setLngLat([90.39, 23.82])
.setHTML('DhakaCapital of Bangladesh')
.addTo(map)
// Popup with options
const popup = new bkoigl.Popup({
closeButton: true,
closeOnClick: false,
offset: 25,
anchor: 'bottom',
})
.setLngLat([90.39, 23.82])
.setText('Hello World!')
.addTo(map)
```
### Marker with Popup
Attach a popup to a marker that opens when clicked.
```javascript
const popup = new bkoigl.Popup({ offset: 25 }).setHTML('LocationThis is a marker')
const marker = new bkoigl.Marker().setLngLat([90.39, 23.82]).setPopup(popup).addTo(map)
// Toggle popup programmatically
marker.togglePopup()
```
---
## Map Controls
### Navigation Control
Adds zoom in/out buttons and a compass for rotation.
```javascript
// Add navigation control
map.addControl(new bkoigl.NavigationControl(), 'top-right')
// With options
map.addControl(
new bkoigl.NavigationControl({
visualizePitch: true, // Show pitch visualization
showZoom: true,
showCompass: true,
}),
'top-right'
)
```
### Geolocate Control
Allows users to find and track their current location.
```javascript
map.addControl(
new bkoigl.GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
},
trackUserLocation: true, // Track user movement
showAccuracyCircle: true,
showUserHeading: true,
}),
'top-right'
)
```
### Scale Control
Displays a scale bar showing distances.
```javascript
map.addControl(
new bkoigl.ScaleControl({
maxWidth: 100,
unit: 'metric', // 'metric', 'imperial', or 'nautical'
}),
'bottom-left'
)
```
### Fullscreen Control
Allows users to toggle fullscreen mode.
```javascript
map.addControl(new bkoigl.FullscreenControl(), 'top-right')
```
### Minimap Control
> **Info:** The minimap can be configured via the `minimap` option in Map constructor or added using `addControl()`.
Adds a small overview map that syncs with the main map, providing geographic context by showing the surrounding area at a different zoom level.
#### Basic Usage
```javascript
// Using map options
const map = new bkoigl.Map({
container: 'map',
accessToken: 'YOUR_BARIKOI_API_KEY',
center: [90.39, 23.82],
zoom: 12,
minimap: {
zoomAdjust: -4,
position: 'bottom-right',
},
})
// Or using addControl
const minimap = new bkoigl.Minimap({
zoomAdjust: -4,
position: 'bottom-right',
})
map.addControl(minimap, 'bottom-right')
```
#### Minimap Options
| Option | Type | Default | Description |
| ------------------ | ---------------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- |
| `style` | string \| StyleSpecification | _parent style_ | Map style for the minimap. If not provided, inherits from parent map |
| `zoomAdjust` | number | `-4` | Zoom level difference between parent and minimap. Positive = zoomed out more |
| `lockZoom` | number | _none_ | Lock minimap to a specific zoom level (overrides zoomAdjust) |
| `pitchAdjust` | boolean | `false` | Whether to sync pitch (tilt) with parent map |
| `position` | string | `'top-right'` | Position of minimap control (`'top-left'`, `'top-right'`, `'bottom-left'`, `'bottom-right'`) |
| `center` | [number, number] | _parent center_ | Initial center coordinates [lng, lat] |
| `accessToken` | string | _parent token_ | Barikoi API access token for the minimap |
| `containerStyle` | object | `{ width: '400px', height: '300px' }` | Custom CSS properties for the minimap container |
| `borderRadius` | string | `'3px'` | Border radius of the minimap container |
| `toggleable` | boolean | `true` | Whether the minimap can be minimized/maximized |
| `toggleButton` | object | _default button_ | Custom toggle button configuration |
| `initialMinimized` | boolean | `false` | Whether to start in minimized state |
| `collapsedWidth` | string | `'29px'` | Width when minimized |
| `collapsedHeight` | string | `'29px'` | Height when minimized |
| `hideText` | string | `'Hide minimap'` | Tooltip text when expanded |
| `showText` | string | `'Show minimap'` | Tooltip text when minimized |
| `onToggle` | function | _none_ | Callback when minimap is toggled: `(isMinimized: boolean) => void` |
| `interactions` | object | _all disabled_ | Map interactions configuration |
| `parentRect` | object | _none_ | Parent rectangle overlay configuration |
| `responsive` | boolean | `true` | Enable responsive sizing based on window dimensions |
| `responsiveWidth` | string | `'20vw'` | Responsive width as CSS value (e.g., '20vw', '30%', '300px') |
| `responsiveHeight` | string | `'20vh'` | Responsive height as CSS value (e.g., '20vh', '30%', '200px') |
| `minWidth` | string | `'200px'` | Minimum width constraint for responsive sizing |
| `minHeight` | string | `'150px'` | Minimum height constraint for responsive sizing |
| `maxWidth` | string | `'400px'` | Maximum width constraint for responsive sizing |
| `maxHeight` | string | `'300px'` | Maximum height constraint for responsive sizing |
### Removing Controls
```javascript
const control = new bkoigl.NavigationControl()
map.addControl(control, 'top-right')
// Remove the control
map.removeControl(control)
```
---
## Camera Methods
### Fly To
Smooth animated transition to a location with a "flying" effect.
```javascript
map.flyTo({
center: [90.39, 23.82],
zoom: 14,
bearing: 0,
pitch: 0,
speed: 1.2, // Animation speed
curve: 1.42, // Flying curve
})
```
### Ease To
Smooth animated transition with customizable duration.
```javascript
map.easeTo({
center: [90.39, 23.82],
zoom: 14,
bearing: 45,
pitch: 30,
duration: 2000, // Duration in ms
})
```
### Jump To
Instant transition without animation.
```javascript
map.jumpTo({
center: [90.39, 23.82],
zoom: 14,
bearing: 0,
pitch: 0,
})
```
### Pan To
Pan the map to a location.
```javascript
map.panTo([90.39, 23.82], { duration: 1000 })
```
### Zoom Methods
```javascript
map.setZoom(14)
map.zoomTo(15, { duration: 500 })
map.zoomIn({ duration: 500 })
map.zoomOut({ duration: 500 })
```
### Rotation Methods
```javascript
map.setBearing(45)
map.rotateTo(90, { duration: 500 })
map.resetNorth({ duration: 500 })
```
### Pitch Methods
```javascript
map.setPitch(45)
```
### Fit Bounds
Fit the map to show a specific area.
```javascript
const bounds = [
[90.3, 23.7],
[90.5, 23.9],
] // [SW, NE]
map.fitBounds(bounds, {
padding: 50, // Padding in pixels
duration: 2000,
})
```
### Get Camera State
```javascript
const center = map.getCenter() // { lng, lat }
const zoom = map.getZoom() // number
const bearing = map.getBearing() // number
const pitch = map.getPitch() // number
const bounds = map.getBounds() // { getWest, getSouth, getEast, getNorth }
```
---
## Custom Layers & Sources
### Adding a GeoJSON Source
```javascript
map.addSource('my-source', {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [90.39, 23.82],
},
properties: {
title: 'Dhaka',
},
},
],
},
})
```
### Adding Layers
#### Circle Layer (Points)
```javascript
map.addLayer({
id: 'my-circle-layer',
type: 'circle',
source: 'my-source',
paint: {
'circle-radius': 10,
'circle-color': '#ff0000',
'circle-opacity': 0.8,
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff',
},
})
```
#### Line Layer
```javascript
map.addLayer({
id: 'my-line-layer',
type: 'line',
source: 'my-source',
layout: {
'line-cap': 'round',
'line-join': 'round',
},
paint: {
'line-color': '#0088ff',
'line-width': 3,
'line-opacity': 0.8,
},
})
```
#### Fill Layer (Polygons)
```javascript
map.addLayer({
id: 'my-fill-layer',
type: 'fill',
source: 'my-source',
paint: {
'fill-color': '#28a745',
'fill-opacity': 0.5,
},
})
// Add outline for the polygon
map.addLayer({
id: 'my-fill-outline',
type: 'line',
source: 'my-source',
paint: {
'line-color': '#ffffff',
'line-width': 2,
},
})
```
### Layer Visibility
```javascript
// Hide a layer
map.setLayoutProperty('my-layer', 'visibility', 'none')
// Show a layer
map.setLayoutProperty('my-layer', 'visibility', 'visible')
```
### Removing Sources & Layers
```javascript
// Remove a layer
map.removeLayer('my-layer')
// Remove a source (remove layers first)
map.removeSource('my-source')
```
### Querying Layers
```javascript
// Check if layer exists
if (map.getLayer('my-layer')) {
map.removeLayer('my-layer')
}
// Check if source exists
if (map.getSource('my-source')) {
map.removeSource('my-source')
}
```
---
## Utility Methods
### Map State
```javascript
// Get bounds
const bounds = map.getBounds()
console.log(bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth())
// Set max bounds
map.setMaxBounds([
[90.0, 23.5],
[91.0, 24.5],
])
// Get projection
const projection = map.getProjection()
// World copies
map.setRenderWorldCopies(true)
```
### Resize
Trigger a map resize when the container size changes.
```javascript
map.resize()
```
### Interaction Handlers
Enable or disable specific map interactions:
```javascript
// Pattern: map.handler.enable() / map.handler.disable()
map.scrollZoom.enable()
map.scrollZoom.disable()
```
| Handler | Description |
| ----------------- | -------------------- |
| `scrollZoom` | Scroll wheel zooming |
| `dragPan` | Drag to pan |
| `dragRotate` | Drag to rotate |
| `keyboard` | Keyboard navigation |
| `doubleClickZoom` | Double-click zoom |
| `touchZoomRotate` | Touch zoom/rotate |
| `touchPitch` | Touch pitch gestures |
| `boxZoom` | Box zoom selection |
---
## Examples
Explore our interactive code examples with live demos and source code, covering basic maps to advanced features like markers, popups, layers, styling and animations.
**[Interactive Examples](https://docs.barikoi.com/examples)**
---
## Documentation
- [Map API Reference](https://docs.barikoi.com/docs/API%20Reference/bkoi-map) - Complete Barikoi GL JS API documentation
- [Business API Reference](https://docs.barikoi.com/api) - Barikoi location and business APIs
---
## Support Resources
- [Barikoi Documentation](https://docs.barikoi.com/docs/maps-api)
- [MapLibre GL JS Docs](https://maplibre.org/maplibre-gl-js/docs/)
- [GitHub Issues](https://github.com/barikoi/bkoi-gl-js/issues)
- [Barikoi Support](mailto:support@barikoi.com)
---
## License
This library is licensed under the MIT License. See the [LICENSE](https://github.com/barikoi/bkoi-gl-js/blob/main/LICENSE) file for details.
---
## Example: Using Markers with Barikoi GL
### Introduction
This guide demonstrates how to add a `customizable marker` to a map using the `bkoi-gl` package within a React component. The example shows how to create a draggable marker, define its color, and position it on the map. Markers are useful for pinpointing specific locations on the map and can be easily customized based on your application's requirements.
### Example
### Instructions
**Install the `bkoi-gl` Package**
Use the following code in your React component to display a map with a draggable marker:
```js
"use client";
const MainMap = () => {
// Refs
const mapContainer = useRef(null);
const map = useRef(null);
useEffect(() => {
if (map.current) return; // Ensures map initializes only once
// Initialize the map
map.current = new Map({
container: mapContainer.current,
center: [90.39017821904588, 23.719800220780733], // Dhaka coordinates
zoom: 10,
doubleClickZoom: false,
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Replace with your Barikoi API key
});
// Initialize and add the marker after the map is created
const marker = new Marker({
color: "#ed6a5a", // Marker color
draggable: true, // Make the marker draggable if you make it false the marker will not be draggable
})
.setLngLat([90.39017821904588, 23.719800220780733]) // Marker coordinates
.addTo(map.current); // Add marker to the map
}, []);
return ;
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default MainMap;
```
---
## Example: Controls
### FullscreenControl
The `FullscreenControl` is the component provided by the `bkoi-gl` package that allows users to toggle fullscreen mode for the map. This can enhance the user experience by providing a more immersive view of the map.
### NavigationControl
The `NavigationControl` component in the `bkoi-gl` package provides users with convenient map navigation tools, including zoom in/out controls and a reset-to-north feature. These controls improve user interaction by offering an easy way to adjust the map view, allowing users to zoom in for detailed exploration or zoom out for a broader perspective. Additionally, the reset-to-north functionality helps reorient the map to its default position, ensuring a more seamless and intuitive mapping experience.
### GeolocateControl
The `GeolocateControl` component in the `bkoi-gl` package allows users to center the map on their current location. By leveraging the device's GPS capabilities, it provides a seamless way for users to quickly view their position on the map. This feature is particularly useful for location-based services, navigation, and enhancing user experience in applications where finding one's location is crucial. The control ensures users can easily orient themselves within the map, improving engagement and interactivity.
### ScaleControl
The `ScaleControl` component in the `bkoi-gl` package displays a scale bar on the map, helping users understand distances between points on the map. This visual guide dynamically adjusts as users zoom in or out, offering a clear representation of scale relative to the map’s current zoom level. It enhances map usability by providing a quick reference for measuring distances, making it especially useful in mapping, navigation, and geographic analysis applications. The ScaleControl ensures users can easily gauge real-world distances, improving the overall user experience.
### Example
```js
"use client";
Map,
FullscreenControl,
GeolocateControl,
NavigationControl,
} from "bkoi-gl";
// MainMap component
const MainMap = () => {
// Reference to the map container DOM element
const mapContainer = useRef(null);
// Reference to the map instance
const map = useRef(null);
useEffect(() => {
// Check if the map is already initialized
if (map.current) return;
// Initialize the map
map.current = new Map({
container: mapContainer.current, // Reference to the map container element
center: [90.39017821904588, 23.719800220780733], // Coordinates for center of the map (Dhaka)
zoom: 10, // Initial zoom level
doubleClickZoom: false, // Disable zooming on double-click
accessToken: "YOUR_BARIKOI_API_KEY_HERE", // Your Barikoi API key
});
// Add fullscreen control to the map
map.current.addControl(new FullscreenControl());
map.current.addControl(new NavigationControl());
map.current.addControl(new GeolocateControl());
}, []); // Empty dependency array ensures this effect runs only once on component mount
return ;
};
// Styles for the map container
const containerStyles = {
width: "100%", // Full width of the container
height: "100vh", // Full viewport height
minHeight: "400px", // Minimum height of 400px
overflow: "hidden", // Hide overflow to prevent scrollbars
};
export default MainMap;
```
---
## Example: Draw polygon with bkoi-gl
### Introduction
This project demonstrates how to integrate a map using the `bkoi-gl` library and allows users to add, update, and delete `polygon layers` interactively.
Before using this project, ensure the following:
1. **Node.js**: Installed on your system.
2. **API Key**: Obtain a valid Barikoi API key from [Barikoi](https://developer.barikoi.com/).
3. **Bkoi-gl package**: The polygon drawing tool works only with version 2.0.2 or later. Please ensure using latest version.
### Example
```js
'use client';
const MainMap = () => {
const mapContainer = useRef(null);
const map = useRef(null);
useEffect(() => {
if (map.current) return; // Ensures map initializes only once
// Initialize the map
map.current = new Map({
container: mapContainer.current,
center: [90.39017821904588, 23.719800220780733], // Dhaka coordinates
zoom: 10,
doubleClickZoom: false,
accessToken: '< Barikoi API key >', // Replace with your
// Barikoi API key
polygon: true, //Enable Polygon Drawing
});
// Listen to events
map.current.on('draw.create', (e) => {
console.log('Polygon created:', e.features);
});
map.current.on('draw.update', (e) => {
console.log('Polygon updated:', e.features);
});
map.current.on('draw.delete', (e) => {
console.log('Polygon deleted:', e.features);
});
}, []);
return ;
};
// JSX Styles
const containerStyles = {
width: '100%',
height: '100vh',
minHeight: '400px',
overflow: 'hidden',
};
export default MainMap;
```
### How it will look after implementation

### Enable Polygon Drawing
The polygon property in the map configuration enables polygon drawing functionality. When enabled, users can draw polygons directly on the map interface.
## Listen to Polygon Events
The map listens for three main events related to polygons:
- draw.create: Triggered when a new polygon is created.
Example:
```javascript
map.current.on('draw.create', (e) => {
console.log('Polygon created:', e.features);
});
```
- draw.update: Triggered when an existing polygon is updated.
Example:
```javascript
map.current.on('draw.update', (e) => {
console.log('Polygon updated:', e.features);
});
```
- draw.delete: Triggered when a polygon is deleted.
Example:
```javascript
map.current.on('draw.delete', (e) => {
console.log('Polygon deleted:', e.features);
});
```
---
## Example: Controls(Barikoi React GL)
### FullscreenControl
The `FullscreenControl` is the component provided by the `react-bkoi-gl` package that allows users to toggle fullscreen mode for the map. This can enhance the user experience by providing a more immersive view of the map.
### NavigationControl
The `NavigationControl` component in the `react-bkoi-gl` package provides users with convenient map navigation tools, including zoom in/out controls and a reset-to-north feature. These controls improve user interaction by offering an easy way to adjust the map view, allowing users to zoom in for detailed exploration or zoom out for a broader perspective. Additionally, the reset-to-north functionality helps reorient the map to its default position, ensuring a more seamless and intuitive mapping experience.
### GeolocateControl
The `GeolocateControl` component in the `react-bkoi-gl` package allows users to center the map on their current location. By leveraging the device's GPS capabilities, it provides a seamless way for users to quickly view their position on the map. This feature is particularly useful for location-based services, navigation, and enhancing user experience in applications where finding one's location is crucial. The control ensures users can easily orient themselves within the map, improving engagement and interactivity.
### ScaleControl
The `ScaleControl` component in the `react-bkoi-gl` package displays a scale bar on the map, helping users understand distances between points on the map. This visual guide dynamically adjusts as users zoom in or out, offering a clear representation of scale relative to the map’s current zoom level. It enhances map usability by providing a quick reference for measuring distances, making it especially useful in mapping, navigation, and geographic analysis applications. The ScaleControl ensures users can easily gauge real-world distances, improving the overall user experience.
### Example
```js
"use client";
Map,
FullscreenControl,
NavigationControl,
GeolocateControl,
ScaleControl,
} from "react-bkoi-gl"; // Import the Barikoi React GL package
const App = () => {
const BARIKOI_API_KEY = "YOUR_BARIKOI_API_KEY_HERE";
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${BARIKOI_API_KEY}`;
const mapContainer = useRef(null);
const mapRef = useRef(null);
const initialViewState = {
longitude: 90.36402,
latitude: 23.823731,
minZoom: 4,
maxZoom: 30,
zoom: 13,
bearing: 0,
pitch: 0,
antialias: true,
};
return (
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default App;
```
---
## Example: Displaying a Deck GL Layer with Barikoi React GL
The `GeoJsonLayer` in this example demonstrates how to integrate Deck GL with the `react-bkoi-gl` package, providing a powerful way to visualize geographic data on a map. The `GeoJsonLayer` is designed to render GeoJSON data on top of a Barikoi map using the `MapboxOverlay` from Deck GL, allowing for highly customizable map layers.
By leveraging `react-bkoi-gl` alongside Deck GL, this example highlights the capability to create rich, layered maps with custom geo-referenced data, enhancing mapping and geospatial applications for diverse use cases such as urban planning, transportation, and location analysis.
### Example
```js
// Create DeckGL Overlay
const DeckGLOverlay = (props) => {
const overlay = useControl(() => new MapboxOverlay(props));
overlay.setProps(props);
return null;
};
const App = () => {
const BARIKOI_API_KEY = "YOUR_BARIKOI_API_KEY_HERE";
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${BARIKOI_API_KEY}`;
const mapContainer = useRef(null);
const mapRef = useRef(null);
const [data, setData] = useState(null);
const initialViewState = {
longitude: 90.36402,
latitude: 23.823731,
minZoom: 4,
maxZoom: 30,
zoom: 6,
bearing: 0,
pitch: 0,
antialias: true,
};
useEffect(() => {
// Fetching GeoJSON data
fetch("/data.json")
.then((response) => response.json())
.then((jsonData) => setData(jsonData))
.catch((error) => console.error("Error loading GeoJSON data:", error));
}, []);
const layers = new GeoJsonLayer({
id: "GeoJsonLayer",
data,
stroked: false,
filled: true,
pointType: "circle+text",
pickable: true,
lineWidthScale: 3,
lineWidthMaxPixels: 5,
lineWidthMinPixels: 1,
getFillColor: [160, 160, 180, 200],
wireframe: true,
});
return (
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default App;
```
---
## Example: Using Draggable Markers with Barikoi React GL
The `Marker` component in the `react-bkoi-gl` package enables users to place and drag markers on the map. This interactive feature allows users to reposition markers by dragging them, providing a dynamic way to update locations. Ideal for applications requiring flexible marker placement, the `Marker` component enhances user interaction and map customization.
### Example
```js
"use client";
const App = () => {
const BARIKOI_API_KEY = "YOUR_BARIKOI_API_KEY_HERE";
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${BARIKOI_API_KEY}`;
const mapContainer = useRef(null);
const mapRef = useRef(null);
const initialViewState = {
longitude: 90.36402,
latitude: 23.823731,
minZoom: 4,
maxZoom: 30,
zoom: 13,
bearing: 0,
pitch: 0,
antialias: true,
};
const [marker, setMarker] = useState({
latitude: initialViewState.latitude,
longitude: initialViewState.longitude,
});
const [events, logEvents] = useState({});
// On marker drag
const onMarkerDrag = useCallback((event) => {
logEvents((_events) => ({ ..._events, onDrag: event.lngLat }));
setMarker({
longitude: event.lngLat.lng,
latitude: event.lngLat.lat,
});
}, []);
// On marker drag start
const onMarkerDragStart = useCallback((event) => {
logEvents((_events) => ({ ..._events, onDragStart: event.lngLat }));
}, []);
// On marker drag end
const onMarkerDragEnd = useCallback((event) => {
logEvents((_events) => ({ ..._events, onDragEnd: event.lngLat }));
}, []);
return (
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default App;
```
---
## Example: Drawing a Polygon with Barikoi React GL using Mapbox Draw Tool
The `Draw Polygon` feature in the `react-bkoi-gl` package, powered by the Mapbox Draw tool, allows users to create and manipulate polygons directly on a Barikoi map. By integrating the `DrawControl` component, users can draw custom shapes by selecting the polygon tool, and they can easily update or delete them as needed.
This feature is ideal for use cases like defining custom regions, creating geofences, or marking specific boundaries on a map. With real-time feedback, users can interactively create precise polygonal areas, offering a hands-on, flexible approach for mapping and geospatial applications. The combination of `react-bkoi-gl` and `Mapbox Draw` makes it simple to implement advanced drawing functionality within a seamless mapping experience.
### Example
```js
const App = () => {
const BARIKOI_API_KEY = "YOUR_BARIKOI_API_KEY_HERE";
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${BARIKOI_API_KEY}`;
const mapContainer = useRef(null);
const mapRef = useRef(null);
const initialViewState = {
longitude: 90.36402,
latitude: 23.823731,
minZoom: 4,
maxZoom: 30,
zoom: 11,
bearing: 0,
pitch: 0,
antialias: true,
};
// Stores all the drawn features
const [features, setFeatures] = useState({});
// On Update
const onUpdate = useCallback((e) => {
setFeatures((currFeatures) => {
const newFeatures = { ...currFeatures };
for (const f of e.features) {
newFeatures[f.id] = f;
}
return newFeatures;
});
}, []);
// On Delete
const onDelete = useCallback((e) => {
setFeatures((currFeatures) => {
const newFeatures = { ...currFeatures };
for (const f of e.features) {
delete newFeatures[f.id];
}
return newFeatures;
});
}, []);
return (
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default App;
```
```js
export default function DrawControl(props) {
useControl(
() => new MapboxDraw(props),
({ map }) => {
map.on("draw.create", props.onCreate);
map.on("draw.update", props.onUpdate);
map.on("draw.delete", props.onDelete);
},
({ map }) => {
map.off("draw.create", props.onCreate);
map.off("draw.update", props.onUpdate);
map.off("draw.delete", props.onDelete);
},
{
position: props.position,
}
);
return null;
}
DrawControl.defaultProps = {
onCreate: () => {},
onUpdate: () => {},
onDelete: () => {},
};
```
---
## Example: Using Markers with Barikoi React GL
The `Marker` component in the `react-bkoi-gl` package allows developers to place customizable markers at specific geographic coordinates on the map. These markers can represent points of interest, locations, or other notable spots, offering a clear visual indicator for users. With the ability to style and position markers flexibly, the `Marker` component enhances the map’s interactivity by making it easier for users to identify important locations. This feature is essential for applications that require pinpointing places, such as navigation, event tracking, or location-based services.
### Example
```js
"use client";
const App = () => {
const BARIKOI_API_KEY = "YOUR_BARIKOI_API_KEY_HERE";
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${BARIKOI_API_KEY}`;
const mapContainer = useRef(null);
const mapRef = useRef(null);
const initialViewState = {
longitude: 90.36402,
latitude: 23.823731,
minZoom: 4,
maxZoom: 30,
zoom: 13,
bearing: 0,
pitch: 0,
antialias: true,
};
return (
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default App;
```
---
## Example: Displaying a Map with Barikoi React GL
Here’s an example of how to use the `react-bkoi-gl` package in a React or Next.js component.
### Example
```js
"use client";
const App = () => {
const BARIKOI_API_KEY = "YOUR_BARIKOI_API_KEY_HERE";
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${BARIKOI_API_KEY}`;
const mapContainer = useRef(null);
const mapRef = useRef(null);
const initialViewState = {
longitude: 90.36402,
latitude: 23.823731,
minZoom: 4,
maxZoom: 30,
zoom: 13,
bearing: 0,
pitch: 0,
antialias: true,
};
return (
);
};
// JSX Styles
const containerStyles = {
width: "100%",
height: "100vh",
minHeight: "400px",
overflow: "hidden",
};
export default App;
```
---
## React Bkoi GL
[](https://www.npmjs.com/package/react-bkoi-gl)
[](https://www.npmjs.com/package/react-bkoi-gl)
[](https://bundlephobia.com/package/react-bkoi-gl)
[](https://www.typescriptlang.org/)
[](https://nodejs.org/)
[](https://opensource.org/licenses/MIT)
---
## Description
`react-bkoi-gl` is a suite of React components that provides a React API for Barikoi Maps. Built on top of MapLibre GL JS, it offers high-performance, customizable map rendering with full TypeScript support.
Powered by Barikoi - Maps for Businesses
---
## Features
- High-performance map rendering using WebGL
- Easy integration with React and Next.js
- Customizable map controls and interactions
- Drawing tools for polygons, lines, and points
- Minimap control for navigation
- Support for GeoJSON, vector tiles, and raster sources
- Full TypeScript support
- Lightweight and optimized for production
---
## Get Barikoi API Key
To access Barikoi's API services, you need to:
1. Register on [Barikoi Developer Dashboard](https://developer.barikoi.com/register)
2. Verify with your phone number
3. Claim your API key
---
## Installation
Using `react-bkoi-gl` requires `react >= 16.3`.
```bash
npm install react-bkoi-gl
```
Or via yarn:
```bash
yarn add react-bkoi-gl
```
Import styles in your application:
```javascript
```
---
## Quick Start
```tsx
const BARIKOI_API_KEY = 'YOUR_BARIKOI_API_KEY';
function App() {
return (
);
}
```
---
## Components
Build maps by composing the `Map` component with layers, sources, UI controls, and hooks.
### Quick Index
#### Core
- [`Map`](#map-component): Core map component. Parent of all other components.
- [`Marker`](#marker-component): Marker at a coordinate (supports custom children).
- [`Popup`](#popup-component): Popup UI at a coordinate or attached to a marker.
#### Data & Rendering
- [`Source`](#source-component): Data source (GeoJSON, vector, raster, image, video, etc.).
- [`CanvasSource`](#canvas-source): Render a custom HTML canvas as a source.
- [`Layer`](#layer-component): Render MapLibre layers from a source (supports events).
#### Controls
- [`NavigationControl`](#navigation-control): Zoom/compass controls.
- [`FullscreenControl`](#fullscreen-control): Fullscreen toggle.
- [`GeolocateControl`](#geolocate-control): Locate and track the user.
- [`ScaleControl`](#scale-control): Scale bar.
- [`TerrainControl`](#terrain-control): Terrain visualization.
- [`DrawControl`](#draw-control): Draw/edit polygons, lines, points.
- [`GlobeControl`](#globe-control): Toggle globe projection (MapLibre 3.x+).
- [`MinimapControl`](#minimap-control): Overview minimap (toggleable, responsive).
#### Hooks
- [`useMap`](#usemap-hook): Access map instances via `MapProvider`.
- [`useControl`](#usecontrol-hook): Create custom controls.
---
### Map Component
The core component that renders a Barikoi map. All other components must be children of `Map`.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `mapStyle` | `string \| StyleSpecification` | Required | Map style URL or style object |
| `initialViewState` | `object` | - | Initial view state (longitude, latitude, zoom, etc.) |
| `viewState` | `object` | - | Controlled view state |
| `mapLib` | `MapLib \| Promise` | - | Custom map library instance |
| `reuseMaps` | `boolean` | `false` | Reuse map instances for performance |
| `id` | `string` | - | Container element ID |
| `style` | `CSSProperties` | - | Container CSS styles |
| `children` | `ReactNode` | - | Child components |
| `onClick` | `(e: MapLayerMouseEvent) => void` | - | Click handler |
| `onLoad` | `(e: MapLibreEvent) => void` | - | Map load handler |
| `onMoveEnd` | `(e: ViewStateChangeEvent) => void` | - | Move end handler |
| `onZoomEnd` | `(e: ViewStateChangeEvent) => void` | - | Zoom end handler |
| `onError` | `(e: ErrorEvent) => void` | - | Error handler |
And all [MapLibre Map options](https://maplibre.org/maplibre-gl-js/docs/API/types/MapOptions/).
#### Example
```tsx
function MapExample() {
const mapRef = useRef(null);
const handleMoveEnd = (e) => {
const map = mapRef.current?.getMap();
if (map) {
console.log('Center:', map.getCenter());
console.log('Zoom:', map.getZoom());
}
};
return (
);
}
```
---
### Marker Component
Displays a marker on the map at specified coordinates.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `longitude` | `number` | Required | Longitude of the marker |
| `latitude` | `number` | Required | Latitude of the marker |
| `color` | `string` | - | Marker color |
| `draggable` | `boolean` | `false` | Enable dragging |
| `offset` | `[number, number]` | - | Pixel offset |
| `anchor` | `string` | `'center'` | Marker anchor position |
| `scale` | `number` | `1` | Marker scale |
| `rotation` | `number` | `0` | Rotation in degrees |
| `rotationAlignment` | `string` | `'auto'` | Rotation alignment mode |
| `pitchAlignment` | `string` | `'auto'` | Pitch alignment mode |
| `popup` | `Popup` | - | Popup to show on click |
| `onClick` | `(e: MarkerEvent) => void` | - | Click handler |
| `onDragStart` | `(e: MarkerDragEvent) => void` | - | Drag start handler |
| `onDrag` | `(e: MarkerDragEvent) => void` | - | Drag handler |
| `onDragEnd` | `(e: MarkerDragEvent) => void` | - | Drag end handler |
#### Example
```tsx
function MarkerExample() {
const [marker, setMarker] = useState({
lng: 90.3938,
lat: 23.8216
});
const onDragEnd = (e) => {
setMarker({
lng: e.lngLat.lng,
lat: e.lngLat.lat
});
};
return (
);
}
```
---
### Popup Component
Displays a popup with custom content at specified coordinates.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `longitude` | `number` | Required | Longitude of the popup |
| `latitude` | `number` | Required | Latitude of the popup |
| `anchor` | `string` | - | Popup anchor position |
| `offset` | `number \| [number, number]` | - | Pixel offset |
| `className` | `string` | - | CSS class name |
| `maxWidth` | `string` | `'240px'` | Maximum width |
| `closeButton` | `boolean` | `true` | Show close button |
| `closeOnClick` | `boolean` | `true` | Close on map click |
| `onOpen` | `() => void` | - | Open handler |
| `onClose` | `() => void` | - | Close handler |
| `children` | `ReactNode` | - | Popup content |
#### Example
```tsx
function PopupExample() {
const [showPopup, setShowPopup] = useState(true);
return (
);
}
```
---
### Source Component
Defines a data source for the map. Supports GeoJSON, vector tiles, raster tiles, image, and video sources.
Props
| Prop | Type | Description |
|------|------|-------------|
| `id` | `string` | Unique source identifier |
| `type` | `string` | Source type: `'geojson'`, `'vector'`, `'raster'`, `'image'`, `'video'` |
| `data` | `object \| string` | GeoJSON data or URL (for geojson type) |
| `url` | `string` | Tile URL (for vector/raster) |
| `tiles` | `string[]` | Tile URLs array |
| `tileSize` | `number` | Tile size in pixels |
| `coordinates` | `array` | Image coordinates (for image type) |
| `children` | `ReactNode` | Layer components using this source |
#### Example
```tsx
function SourceExample() {
const geojsonData = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'Point A' },
geometry: {
type: 'Point',
coordinates: [90.3938, 23.8216]
}
},
{
type: 'Feature',
properties: { name: 'Point B' },
geometry: {
type: 'Point',
coordinates: [90.40, 23.83]
}
}
]
};
const lineData = {
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[90.3938, 23.8216],
[90.40, 23.83],
[90.41, 23.84]
]
}
};
const polygonData = {
type: 'Feature',
properties: { name: 'Polygon Area' },
geometry: {
type: 'Polygon',
coordinates: [[
[90.39, 23.82],
[90.41, 23.82],
[90.41, 23.84],
[90.39, 23.84],
[90.39, 23.82]
]]
}
};
return (
);
}
```
---
### Layer Component
Renders data from a source on the map. Supports all MapLibre layer types.
Props
| Prop | Type | Description |
|------|------|-------------|
| `id` | `string` | Unique layer identifier |
| `type` | `string` | Layer type: `'fill'`, `'line'`, `'circle'`, `'symbol'`, `'raster'`, `'heatmap'`, `'hillshade'`, `'background'` |
| `source` | `string` | Source ID (inherited from parent Source) |
| `source-layer` | `string` | Source layer (for vector sources) |
| `paint` | `object` | Paint properties |
| `layout` | `object` | Layout properties |
| `filter` | `array` | Filter expression |
| `minzoom` | `number` | Minimum zoom level |
| `maxzoom` | `number` | Maximum zoom level |
| `beforeId` | `string` | Insert before this layer ID |
#### Example
```tsx
function LayerExample() {
const cities = {
type: 'FeatureCollection',
features: [
{ type: 'Feature', properties: { name: 'Dhaka', population: 21000000 }, geometry: { type: 'Point', coordinates: [90.3938, 23.8216] } },
{ type: 'Feature', properties: { name: 'Chittagong', population: 4000000 }, geometry: { type: 'Point', coordinates: [91.8317, 22.3569] } },
{ type: 'Feature', properties: { name: 'Khulna', population: 1500000 }, geometry: { type: 'Point', coordinates: [89.5555, 22.8456] } }
]
};
return (
);
}
```
---
### Navigation Control
Adds zoom and rotation controls to the map.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-right'` | Control position |
| `showCompass` | `boolean` | `true` | Show compass button |
| `showZoom` | `boolean` | `true` | Show zoom buttons |
| `visualizePitch` | `boolean` | `false` | Visualize pitch in compass |
#### Example
```tsx
function NavigationExample() {
return (
);
}
```
---
### Fullscreen Control
Adds a button to toggle fullscreen mode.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-right'` | Control position |
| `container` | `HTMLElement` | - | Custom fullscreen container |
#### Example
```tsx
function FullscreenExample() {
return (
);
}
```
---
### Geolocate Control
Centers the map on the user's current location.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-right'` | Control position |
| `positionOptions` | `object` | `{ enableHighAccuracy: true }` | Geolocation options |
| `fitBoundsOptions` | `object` | `{ maxZoom: 15 }` | Fit bounds options |
| `trackUserLocation` | `boolean` | `false` | Track user location |
| `showAccuracyCircle` | `boolean` | `true` | Show accuracy circle |
| `showUserLocation` | `boolean` | `true` | Show user location marker |
| `onGeolocate` | `(e: GeolocateResultEvent) => void` | - | Geolocate success handler |
| `onError` | `(e: GeolocateErrorEvent) => void` | - | Error handler |
#### Example
```tsx
function GeolocateExample() {
const onGeolocate = (e) => {
console.log('User location:', e.coords);
};
return (
);
}
```
---
### Scale Control
Displays a scale bar on the map.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'bottom-left'` | Control position |
| `maxWidth` | `number` | `100` | Maximum width in pixels |
| `unit` | `string` | `'metric'` | Unit: `'imperial'`, `'metric'`, or `'nautical'` |
#### Example
```tsx
function ScaleExample() {
return (
);
}
```
---
### Terrain Control
Adds terrain visualization to the map.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-right'` | Control position |
| `source` | `string \| object` | - | Terrain source |
#### Example
```tsx
function TerrainExample() {
return (
);
}
```
---
### Draw Control
Adds drawing tools for creating and editing polygons, lines, and points.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-left'` | Control position |
| `displayControlsDefault` | `boolean` | `false` | Show all controls by default |
| `controls` | `object` | `{ polygon: true, trash: true }` | Control visibility |
| `styles` | `array` | - | Custom draw styles |
| `modes` | `object` | - | Custom draw modes |
| `defaultMode` | `string` | `'simple_select'` | Default drawing mode |
| `onDrawCreate` | `(e: DrawEvent) => void` | - | Feature created handler |
| `onDrawDelete` | `(e: DrawEvent) => void` | - | Feature deleted handler |
| `onDrawUpdate` | `(e: DrawEvent) => void` | - | Feature updated handler |
| `onDrawSelectionChange` | `(e: DrawEvent) => void` | - | Selection change handler |
| `onDrawModeChange` | `(e: DrawEvent) => void` | - | Mode change handler |
#### Example
```tsx
function DrawExample() {
const [features, setFeatures] = useState([]);
const onDrawCreate = (e) => {
console.log('Created features:', e.features);
setFeatures(e.features);
};
const onDrawUpdate = (e) => {
console.log('Updated features:', e.features);
setFeatures(e.features);
};
const onDrawDelete = (e) => {
console.log('Deleted features:', e.features);
};
return (
);
}
```
---
### Minimap Control
Displays a small overview map for navigation.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-right'` | Control position |
| `center` | `[number, number]` | - | Initial center coordinates |
| `zoomAdjust` | `number` | `-4` | Zoom offset from parent |
| `lockZoom` | `number` | - | Lock to specific zoom level |
| `pitchAdjust` | `boolean` | `false` | Sync pitch with parent |
| `style` | `string \| StyleSpecification` | - | Custom minimap style |
| `containerStyle` | `object` | - | Custom container styles |
| `toggleable` | `boolean` | `true` | Allow toggling minimap |
| `initialMinimized` | `boolean` | `false` | Start minimized |
| `responsive` | `boolean` | `true` | Enable responsive sizing |
| `interactions` | `object` | - | Interaction configuration |
| `parentRect` | `object` | - | Parent rectangle styling |
| `onToggle` | `(isMinimized: boolean) => void` | - | Toggle callback |
#### Example
```tsx
function MinimapExample() {
const onToggle = (isMinimized) => {
console.log('Minimap minimized:', isMinimized);
};
return (
);
}
```
---
### Globe Control
Toggle between 2D map and 3D globe view (requires MapLibre GL 3.x+).
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `position` | `string` | `'top-right'` | Control position |
| `buttonClassName` | `string` | `'maplibregl-ctrl-globe'` | Button CSS class |
| `buttonTitle` | `string` | `'Toggle Globe View'` | Button tooltip |
| `buttonStyle` | `CSSProperties` | - | Custom button styles |
| `onProjectionChange` | `(isGlobe: boolean) => void` | - | Projection change handler |
#### Example
```tsx
function GlobeExample() {
const handleProjectionChange = (isGlobe) => {
console.log('Globe view:', isGlobe);
};
return (
);
}
```
---
### Canvas Source
Render custom HTML canvas elements as map layers.
Props
| Prop | Type | Description |
|------|------|-------------|
| `id` | `string` | Unique source identifier |
| `coordinates` | `[[lng,lat], [lng,lat], [lng,lat], [lng,lat]]` | Corner coordinates (top-left, top-right, bottom-right, bottom-left) |
| `canvas` | `HTMLCanvasElement` | The canvas element to render |
| `animate` | `boolean` | Whether to animate the canvas |
| `children` | `ReactNode` | Child Layer components |
#### Example
```tsx
function CanvasExample() {
const canvasRef = useRef(null);
const [canvasEl, setCanvasEl] = useState(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.fillStyle = 'rgba(0, 100, 255, 0.5)';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = 'rgba(255, 0, 0, 0.8)';
ctx.beginPath();
ctx.arc(128, 128, 50, 0, 2 * Math.PI);
ctx.fill();
}
setCanvasEl(canvas);
}, []);
return (
);
}
```
---
## Layer Events
The Layer component supports interactive mouse events:
Available Events
| Prop | Type | Description |
|------|------|-------------|
| `onClick` | `(e: MapLayerMouseEvent) => void` | Fired when layer is clicked |
| `onMouseEnter` | `(e: MapLayerMouseEvent) => void` | Fired when mouse enters layer |
| `onMouseLeave` | `() => void` | Fired when mouse leaves layer |
| `onMouseMove` | `(e: MapLayerMouseEvent) => void` | Fired when mouse moves over layer |
| `onMouseDown` | `(e: MapLayerMouseEvent) => void` | Fired on mouse button press |
| `onMouseUp` | `(e: MapLayerMouseEvent) => void` | Fired on mouse button release |
| `onContextMenu` | `(e: MapLayerMouseEvent) => void` | Fired on right-click |
| `onDoubleClick` | `(e: MapLayerMouseEvent) => void` | Fired on double-click |
#### Example
```tsx
function InteractiveLayerExample() {
const [hoveredFeature, setHoveredFeature] = useState(null);
const geojsonData = {
type: 'FeatureCollection',
features: [
{ type: 'Feature', properties: { name: 'Dhaka' }, geometry: { type: 'Point', coordinates: [90.3938, 23.8216] } }
]
};
return (
);
}
```
---
## Hooks
### useMap Hook
Access map instances from any component within the MapProvider.
#### Example
```tsx
function MapButtons() {
const { current: map } = useMap();
const zoomIn = () => {
map?.zoomIn();
};
const zoomOut = () => {
map?.zoomOut();
};
const flyTo = () => {
map?.flyTo({
center: [90.40, 23.83],
zoom: 15,
duration: 2000
});
};
return (
);
}
function App() {
return (
);
}
```
---
### useControl Hook
Create custom map controls.
#### Example
```tsx
class CustomControl {
onAdd(map) {
this.map = map;
this.container = document.createElement('div');
this.container.className = 'custom-control';
this.container.textContent = 'Custom Control';
this.container.style.cssText = `
background: white;
padding: 10px;
border-radius: 4px;
box-shadow: 0 0 0 2px rgba(0,0,0,0.1);
`;
return this.container;
}
onRemove() {
this.container.remove();
}
}
function CustomControlComponent() {
useControl(() => new CustomControl(), { position: 'top-left' });
return null;
}
function App() {
return (
);
}
```
---
## Events
The Map component supports various event callbacks:
Available Events
### Mouse Events
- `onClick` - Map click
- `onDblClick` - Double click
- `onMouseDown` - Mouse down
- `onMouseUp` - Mouse up
- `onMouseMove` - Mouse move
- `onMouseEnter` - Mouse enter
- `onMouseLeave` - Mouse leave
- `onMouseOver` - Mouse over
- `onMouseOut` - Mouse out
- `onContextMenu` - Right click
### Touch Events
- `onTouchStart` - Touch start
- `onTouchEnd` - Touch end
- `onTouchMove` - Touch move
- `onTouchCancel` - Touch cancel
### Movement Events
- `onMoveStart` - Movement start
- `onMove` - Movement
- `onMoveEnd` - Movement end
- `onDragStart` - Drag start
- `onDrag` - Drag
- `onDragEnd` - Drag end
- `onZoomStart` - Zoom start
- `onZoom` - Zoom
- `onZoomEnd` - Zoom end
- `onRotateStart` - Rotation start
- `onRotate` - Rotation
- `onRotateEnd` - Rotation end
- `onPitchStart` - Pitch start
- `onPitch` - Pitch
- `onPitchEnd` - Pitch end
### Map State Events
- `onLoad` - Map loaded
- `onRender` - Frame rendered
- `onIdle` - Map idle
- `onError` - Error occurred
- `onResize` - Map resized
- `onRemove` - Map removed
- `onData` - Data loaded
- `onStyleData` - Style data loaded
- `onSourceData` - Source data loaded
### Event Example
```tsx
function EventExample() {
const [center, setCenter] = useState({ lng: 90.3938, lat: 23.8216 });
const onClick = (e) => {
console.log('Clicked at:', e.lngLat);
setCenter({ lng: e.lngLat.lng, lat: e.lngLat.lat });
};
const onMoveEnd = (e) => {
console.log('Move ended, viewState:', e.viewState);
};
const onLoad = (e) => {
console.log('Map loaded!');
};
return (
);
}
```
---
## MapRef Methods
Access the underlying map instance through the ref:
Available Methods
```tsx
const mapRef = useRef(null);
// Get the underlying MapLibre instance
const map = mapRef.current?.getMap();
// Common methods:
map.getCenter() // Get map center
map.getZoom() // Get zoom level
map.getBearing() // Get bearing
map.getPitch() // Get pitch
map.getBounds() // Get bounds
map.setCenter([lng, lat]) // Set center
map.setZoom(zoom) // Set zoom
map.setBearing(bearing) // Set bearing
map.setPitch(pitch) // Set pitch
map.flyTo(options) // Fly to location
map.jumpTo(options) // Jump to location
map.easeTo(options) // Ease to location
map.zoomIn() // Zoom in
map.zoomOut() // Zoom out
map.resize() // Resize map
map.remove() // Remove map
```
---
## Styling
The library uses MapLibre GL JS styles. Import the styles in your application:
```tsx
```
### Available Map Styles
- `osm-liberty` - Default street style
- `osm_barikoi_v2` - Barikoi street style
```tsx
const mapStyle = `https://map.barikoi.com/styles/osm-liberty/style.json?key=${API_KEY}`;
const mapStyle = `https://map.barikoi.com/styles/osm_barikoi_v2/style.json?key=${API_KEY}`;
```
---
## TypeScript
Full TypeScript support is included. All types are exported:
```tsx
MapProps,
MapRef,
MarkerProps,
PopupProps,
SourceProps,
CanvasSourceProps,
LayerProps,
MapLayerMouseEvent,
ViewStateChangeEvent,
DrawControlProps,
GlobeControlProps,
MinimapControlOptions
} from 'react-bkoi-gl';
```
---
## Documentation & Resources
- [Barikoi API Documentation](https://docs.barikoi.com/docs/maps-api)
- [Barikoi Business API](https://docs.barikoi.com/api)
- [MapLibre GL JS Docs](https://maplibre.org/maplibre-native/ios/latest/documentation/maplibre/)
- [Interactive Examples](https://docs.barikoi.com/examples)
---
## License
This library is licensed under the MIT License. See the [LICENSE](https://github.com/barikoi/react-bkoi-gl/blob/master/LICENSE) file for details.
## Support
For issues or questions, contact [support@barikoi.com](mailto:support@barikoi.com).
---
## Example: Displaying a Map with Barikoi React GL(Widget)
# 📚 **Barikoi Map Widget**
The Barikoi Map Widget simplifies the integration of autocomplete and map functionalities into your web application. It provides seamless connectivity between the autocomplete input and the map display, eliminating the need for separate handling. This widget is particularly useful for applications that require geolocation features, such as finding places or visualizing locations on a map.
## 📦 Installation
To install the `barikoi-map-widget` library, run:
```bash
npm install barikoi-map-widget
```
### 🛠️ Usage
Wrap your components with the BarikoiMapProvider to provide the necessary context for the widget:
```ts
const App = () => {
return (
);
};
export default App;
```
## 🔍 **BarikoiAutoComplete**
The **BarikoiAutoComplete** component is a customizable search input field with real-time autocomplete suggestions fetched from the **Barikoi API**. It supports **debounced search**, **smooth dropdown animations**, and allows **custom styling** via `className` props.
### Basic Example
```tsx
const BarikoiAutocompleteComponent = () => {
return (
Search
);
};
export default BarikoiAutocompleteComponent;
```
### 📋 **Props**
| **Prop Name** | **Type** | **Required** | **Default** | **Description** |
| ------------- | -------- | ------------ | ----------- | --------------------------------------------------------------- |
| `apiKey` | `string` | ✅ Yes | `-` | Your **Barikoi API key** for fetching autocomplete suggestions. |
| `className` | `object` | ❌ No | `{}` | Object to override default styles using CSS class names. |
#### **className Object Structure**
| **Key** | **Type** | **Description** |
| ----------------- | -------- | ------------------------------------- |
| `container` | `string` | Class for the main container. |
| `input` | `string` | Class for the input field. |
| `dropdown` | `string` | Class for the dropdown container. |
| `dropdownVisible` | `string` | Class for the dropdown visibility. |
| `dropdownItem` | `string` | Class for each dropdown item. |
| `noResult` | `string` | Class for "No Results Found" message. |
| `clearButton` | `string` | Class for clear button. |
### 🎨 **Styling**
The component uses **default inline styles** but allows **className** overrides. Below is an example of CSS customizations:
```css
/* customStyles.css */
.map-container {
width: 100%;
height: 98vh;
}
/* customStyles.css */
.custom-container {
/* border: 2px solid blue; */
border-radius: 6px;
}
.custom-input {
font-size: 16px;
border: 1px solid #aaa;
}
.custom-dropdown {
border: 1px solid green;
background-color: #f9fafb;
width: '99.5%';
/* height: 600px; */
max-height: 400px;
}
.custom-noresult {
width: 378px;
}
.custom-dropdown-item {
padding: 12px;
/* font-weight: bold; */
}
.custom-no-result {
color: red;
font-style: italic;
}
.fullContainer {
position: relative;
}
```
### 📊 **State Management**
The BarikoiAutoComplete component uses the following state variables to manage its internal state:
- `searchedPlace`: Tracks the current search input value.
- `setSearchedPlace`: A function to update the searchedPlace state.
- `selectedPlace`: Stores the place that the user has selected from the autocomplete suggestions.
- `setSelectedPlace`: A function to update the selectedPlace state.
- `suggestions`: Contains the list of autocomplete suggestions fetched from the API.
- `setSuggestions`: A function to update the suggestions state.
- `isAutocompleteLoading`: A boolean that indicates whether the autocomplete data is being fetched.
- `setIsAutocompleteLoading`: A function to update the isAutocompleteLoading state.
## 🗺️ **BarikoiMap**
The **BarikoiMap** component is a customizable React component designed to render a map using the Barikoi Maps service. It provides several map controls, such as geolocation, fullscreen mode, and scale controls, and allows for dynamic positioning of a marker. The map’s initial view state and style are customizable, and the component provides an API key to integrate with Barikoi’s map services.
### Basic Example
```tsx
const BarikoiMapComponent = () => {
const BARIKOI_API_KEY = 'YOUR_BARIKOI_API_KEY'; // Replace with your actual API key
const { selectedPlace } = useAutocomplete();
const { setCenterPoint } = useMap();
useEffect(() => {
setCenterPoint({
lat: selectedPlace?.latitude,
lng: selectedPlace?.longitude,
});
}, [selectedPlace]);
// const initialViewState = {
// longitude: 90.36402,
// latitude: 23.823731,
// minZoom: 4,
// maxZoom: 30,
// zoom: 16,
// };
return (
);
};
export default BarikoiMapComponent;
```
### CSS
```css
.map-container {
width: 100%;
height: 100vh;
}
```
`You need to add this to see the map`.
Please change the style as your project requires.
### 📋 **Props**
| **Prop Name** | **Type** | **Required** | **Description** |
| ------------------ | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `apiKey` | `string` | ✅ Yes | Your **Barikoi API key** for fetching autocomplete suggestions. |
| `initialViewState` | `object` | ❌ No | An object defining the initial map view state. Includes properties like `longitude`, `latitude`, `minZoom`, `maxZoom`, `zoom`. |
| `controls` | `object` | ✅ Yes | A configuration object to enable/disable various map controls. Only the `marker` prop is required, rest of them is optional. |
| `mapStyle` | `string` | ❌ No | The map style to be applied, passed as a string. Example: `osm-liberty`. `osm-bright`, `barikoi-dark`. |
| `className` | `object` | ✅ Yes | Additional class names to apply to the component's container. |
### **Control Prop Structure:**
```tsx
const controls = {
geolocate: { enabled: true, position: 'top-left' },
fullscreen: { enabled: true, position: 'top-right' },
navigation: { enabled: true, position: 'bottom-left' },
scale: { enabled: true, position: 'bottom-right' },
marker: { enabled: true, url: 'path-to-marker-icon' },
};
```
### 📊 **State Management**
The map-related state includes:
- `zoomLevel`: The zoom level of the map..
- `setZoomLevel`: A setter function to update the zoom level.
- `centerPoint`: The geographical coordinates of the center point. Contains lat (latitude) and lng (longitude).
- `setCenterPoint`: A setter function to update the center point.
### Full Working Example
Here's the complete example of how to integrate these steps into a React component. This includes the map initialization and the style drawer.
```js
'use client';
export default function Home() {
return (
);
}
```
```js
const BarikoiAutocompleteComponent = () => {
return (
);
};
export default BarikoiAutocompleteComponent;
```
```js
const BarikoiMapComponent = () => {
const BARIKOI_API_KEY = 'BARIKOI_API_KEY'; // Replace with your actual API key
const { selectedPlace } = useAutocomplete();
const { setCenterPoint } = useMap();
useEffect(() => {
setCenterPoint({
lat: selectedPlace?.latitude,
lng: selectedPlace?.longitude,
});
}, [selectedPlace]);
// const initialViewState = {
// longitude: 90.36402,
// latitude: 23.823731,
// minZoom: 4,
// maxZoom: 30,
// zoom: 16,
// };
return (
);
};
export default BarikoiMapComponent;
```
```css
/* Style for the drawer container */
.map-container {
width: 100%;
height: 98vh;
}
/* customStyles.css */
.custom-container {
/* border: 2px solid blue; */
border-radius: 6px;
}
.custom-input {
font-size: 16px;
border: 1px solid #aaa;
}
.custom-dropdown {
border: 1px solid green;
background-color: #f9fafb;
width: '99.5%';
/* height: 600px; */
max-height: 400px;
}
.custom-noresult {
width: 378px;
}
.custom-dropdown-item {
padding: 12px;
/* font-weight: bold; */
}
.custom-no-result {
color: red;
font-style: italic;
}
.fullContainer {
position: relative;
}
```
### What will you get?
By implementing this widget correctly, you will achieve a seamless integration of autocomplete and map functionalities. Below is an example of what the widget will look like in your project.

`You can customize the CSS and manipulate the props to suit your specific needs.`
## Get Barikoi API key
To access Barikoi's API services, you need to:
1. Register on [Barikoi Developer Dashboard](https://developer.barikoi.com/register).
2. Verify with your phone number.
3. Claim your API key.
Once registered, you'll be able to access the full suite of Barikoi API services. If you exceed the free usage limits, you'll need to subscribe to a paid plan.
## Learning Resources
- [Barikoi API Documentation](https://docs.barikoi.com/docs/maps-api)
## License
This library is licensed under the MIT License. See the [LICENSE](https://www.npmjs.com/package/LICENSE) file for details.
## Support
For any issues or questions, please contact [support@barikoi.com](mailto:support@barikoi.com).
---
## geo-polyline-tools
[](https://www.npmjs.com/package/geo-polyline-tools)
[](https://opensource.org/licenses/MIT)
[](https://www.npmjs.com/package/geo-polyline-tools)
[](https://www.npmjs.com/package/geo-polyline-tools)
[](https://www.npmjs.com/package/geo-polyline-tools)
[](https://www.typescriptlang.org/)
A lightweight JavaScript library for encoding and decoding polyline coordinates based on [Google's Encoded Polyline Algorithm](https://developers.google.com/maps/documentation/utilities/polylinealgorithm). Includes full GeoJSON `LineString` support with automatic coordinate axis conversion.
## Features
- **Encode** arrays of `[lat, lng]` coordinates into compressed polyline strings.
- **Decode** polyline strings back into `[lat, lng]` coordinate pairs.
- **GeoJSON conversion** -- encode from and decode to GeoJSON `LineString` geometries with automatic `[lng, lat]` to `[lat, lng]` axis flipping.
- **Configurable precision** -- defaults to 5 decimal places (~1 meter accuracy); adjustable for any use case.
- **Zero dependencies** -- no runtime dependencies, suitable for browser and server environments.
- **TypeScript support** -- type definitions included for both CommonJS and ESM entry points.
- **ES5 compilation target** -- compatible with legacy browsers down to IE11.
## Installation
```bash
npm install geo-polyline-tools
```
Or with yarn:
```bash
yarn add geo-polyline-tools
```
## Compatibility
- **Node.js**: >= 18.19.0
- **Browsers**: All modern browsers and legacy browsers down to IE11 (compiled to ES5)
## Usage
### Importing
**ES Module**
```js
```
**CommonJS**
```js
const polyline = require('geo-polyline-tools');
```
**TypeScript**
```ts
const encoded: string = polyline.encode([[38.5, -120.2], [40.7, -120.95]], 5);
```
**Browser (IIFE)**
Include the script directly, or use a CDN:
```html
```
### Encoding Coordinates
```js
const coords = [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]];
const encoded = polyline.encode(coords, 5);
// => '_p~iF~ps|U_ulLnnqC_mqNvxq`@'
const encodedDefaultPrecision = polyline.encode(coords);
// => '_p~iF~ps|U_ulLnnqC_mqNvxq`@' (precision defaults to 5)
```
### Decoding a Polyline String
```js
const str = '_p~iF~ps|U_ulLnnqC_mqNvxq`@';
const decoded = polyline.decode(str, 5);
// => [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]]
```
### Converting GeoJSON to Polyline
```js
const geojson = {
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [[38.5, -120.2], [40.7, -120.95], [43.252, -126.453]],
},
};
const encoded = polyline.fromGeoJSON(geojson, 5);
// => '~ps|U_p~iFnnqC_ulLvxq`@_mqN'
```
### Converting Polyline to GeoJSON
```js
const str = '_p~iF~ps|U_ulLnnqC_mqNvxq`@';
const geometry = polyline.toGeoJSON(str, 5);
// => {
// type: 'LineString',
// coordinates: [[-120.2, 38.5], [-120.95, 40.7], [-126.453, 43.252]]
// }
```
### Coordinate Ordering
The library uses two different coordinate conventions, matching the standards of each format:
| Method | Coordinate order |
|-----------------------|-------------------------|
| `encode` / `decode` | `[latitude, longitude]` |
| `fromGeoJSON` (input) | `[longitude, latitude]` |
| `toGeoJSON` (output) | `[longitude, latitude]` |
The `encode` and `decode` methods follow Google's Encoded Polyline Algorithm convention, while `fromGeoJSON` and `toGeoJSON` follow the [GeoJSON specification (RFC 7946)](https://tools.ietf.org/html/rfc7946#section-3.1.1), which mandates `[longitude, latitude]` ordering. The library handles the axis conversion automatically.
## API Reference
### `polyline.encode(coordinates, precision?)`
Encodes an array of `[latitude, longitude]` coordinate pairs into a polyline string.
| Parameter | Type | Default | Description |
|----------------|--------------|---------|--------------------------------------------|
| `coordinates` | `number[][]` | -- | Array of `[lat, lng]` pairs. Required. |
| `precision` | `number` | `5` | Number of decimal places to preserve. |
**Returns:** `string` -- the encoded polyline. Returns an empty string when `coordinates` is empty.
### `polyline.decode(str, precision?)`
Decodes a polyline string into an array of `[latitude, longitude]` coordinate pairs.
| Parameter | Type | Default | Description |
|-------------|----------|---------|------------------------------------------------|
| `str` | `string` | -- | The encoded polyline string. Required. |
| `precision` | `number` | `5` | Number of decimal places used during encoding. |
**Returns:** `number[][]` -- array of `[lat, lng]` pairs. Returns an empty array when `str` is empty.
### `polyline.fromGeoJSON(geojson, precision?)`
Encodes a GeoJSON `Feature` or `LineString` geometry into a polyline string. Coordinates are expected in GeoJSON `[longitude, latitude]` ordering and are internally flipped to `[latitude, longitude]` before encoding.
| Parameter | Type | Default | Description |
|-------------|----------|---------|---------------------------------------------------------------------------------------------------------------|
| `geojson` | `object` | -- | A GeoJSON `Feature` with `LineString` geometry, or a `LineString` geometry directly. Required. |
| `precision` | `number` | `5` | Number of decimal places to preserve. |
**Returns:** `string` -- the encoded polyline.
**Throws:** `Error('Input must be a GeoJSON LineString')` if the input is not a valid GeoJSON `LineString` or `Feature` containing one.
### `polyline.toGeoJSON(str, precision?)`
Decodes a polyline string into a GeoJSON `LineString` geometry object. The resulting coordinates follow GeoJSON's `[longitude, latitude]` ordering.
| Parameter | Type | Default | Description |
|-------------|----------|---------|--------------------------------------------------------|
| `str` | `string` | -- | The encoded polyline string. Required. |
| `precision` | `number` | `5` | Number of decimal places used during encoding. |
**Returns:** `{ type: 'LineString', coordinates: number[][] }` -- a GeoJSON `LineString` geometry with `[lng, lat]` coordinates.
## License
This library is licensed under the MIT License. See the [LICENSE](https://github.com/barikoi/geo-polyline-tools/blob/main/LICENSE) file for details.
## Support
For issues, questions, or contributions, visit the [GitHub repository](https://github.com/barikoi/geo-polyline-tools) or contact [hello@barikoi.com](mailto:hello@barikoi.com).
---
---
## Getting Started(React-native)
Complete guide for integrating Barikoi Maps into an existing React Native (Expo) application.
## Prerequisites
- Node.js 18+
- Expo SDK 53+
- An existing Expo project
- Android Studio (for Android) or Xcode (for iOS, macOS only)
- Barikoi API key
---
## Installation
### 1. Get your API key
1. Go to [developer.barikoi.com](https://developer.barikoi.com) and create an account.
2. Navigate to **Dashboard → Account → API Key**.
3. Copy your API key.
### 2. Install dependencies
```bash
npm install @maplibre/maplibre-react-native expo-location expo-constants expo-dev-client barikoiapis
```
If you are using TypeScript, also install the GeoJSON type definitions:
```bash
npm install -D @types/geojson
```
:::info Why `expo-dev-client`?
Barikoi Maps uses native modules that cannot run in Expo Go. `expo-dev-client` lets you create a development build that supports native code.
:::
### 3. Update app.json
Add the following keys **inside your existing `expo` object**. Do not replace your entire `app.json` — merge these in:
```json
{
"expo": {
"plugins": [
"expo-router",
"expo-dev-client",
"expo-location",
"@maplibre/maplibre-react-native"
],
"extra": {
"barikoiApiKey": "YOUR_API_KEY_HERE"
}
}
}
```
:::note
Both `@maplibre/maplibre-react-native` and `expo-location` plugin entries are required — without them the native map module and location permissions will not register correctly.
:::
:::caution New Architecture
If you want to enable the New Architecture, add `"newArchEnabled": true` to your `expo` object. Be aware this may affect other native modules in your project. Test thoroughly before enabling it in an existing project.
:::
### 4. Create the map utilities file
Create `utils/mapUtils.ts` in your project. Adjust the import path in your screen files based on where you place this file.
```typescript
// utils/mapUtils.ts
const apiKey = Constants.expoConfig?.extra?.barikoiApiKey;
// Barikoi API client for search, geocoding, routing
export const barikoiClient = createBarikoiClient({ apiKey });
export const fetchBarikoiMapStyle = async (apiKey: string): Promise => {
const response = await fetch(
`https://map.barikoi.com/styles/barikoi-light-v2/style.json?key=${apiKey}`
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
};
export const useBarikoiMapStyle = () => {
const [styleJson, setStyleJson] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const loadStyle = async () => {
try {
setLoading(true);
setError(null);
const style = await fetchBarikoiMapStyle(apiKey);
setStyleJson(style);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load map style');
} finally {
setLoading(false);
}
};
loadStyle();
}, [apiKey]);
return { styleJson, loading, error };
};
// Default coordinates for Bangladesh cities [longitude, latitude]
export const DEFAULT_COORDINATES = {
DHAKA: [90.364159, 23.823724] as [number, number],
CHITTAGONG: [91.8317, 22.3569] as [number, number],
SYLHET: [91.8833, 24.8949] as [number, number],
RAJSHAHI: [88.6044, 24.3636] as [number, number],
};
// Default camera settings
export const DEFAULT_CAMERA_SETTINGS = {
centerCoordinate: DEFAULT_COORDINATES.DHAKA,
zoomLevel: 16,
animationDuration: 1000,
animationMode: 'linearTo' as const,
};
// Shared map style presets
export const MAP_STYLES = {
line: {
lineColor: '#2e8555',
lineWidth: 3,
lineCap: 'round' as const,
lineJoin: 'round' as const,
},
polygon: {
fillColor: 'rgba(46, 133, 85, 0.5)',
fillOutlineColor: '#2e8555',
},
marker: {
anchorDefault: { x: 0.5, y: 1.0 },
iconSize: { width: 40, height: 40 },
},
};
// Barikoi brand colors
export const BARIKOI_COLORS = {
primary: '#2e8555',
primaryLight: 'rgba(46, 133, 85, 0.5)',
secondary: '#e74c3c',
background: '#f5f5f5',
text: '#333',
white: '#ffffff',
};
// Validate coordinates within Bangladesh bounds
export const isWithinBangladeshBounds = (coordinates: [number, number]): boolean => {
const [lng, lat] = coordinates;
return lat >= 20.3 && lat <= 26.8 && lng >= 88.0 && lng <= 92.8;
};
// Haversine distance between two coordinates (km)
export const calculateDistance = (
coord1: [number, number],
coord2: [number, number],
): number => {
const [lng1, lat1] = coord1;
const [lng2, lat2] = coord2;
const R = 6371;
const dLat = (lat2 - lat1) * Math.PI / 180;
const dLng = (lng2 - lng1) * Math.PI / 180;
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLng / 2) * Math.sin(dLng / 2);
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
};
// Generate circle polygon coordinates
export const createCirclePolygon = (
center: [number, number],
radiusInKm: number,
points: number = 64,
): [number, number][] => {
const coords: [number, number][] = [];
const [lng, lat] = center;
for (let i = 0; i < points; i++) {
const angle = (i * 2 * Math.PI) / points;
const dx = (Math.cos(angle) * radiusInKm) / 111.32;
const dy = (Math.sin(angle) * radiusInKm) / (111.32 * Math.cos(lat * (Math.PI / 180)));
coords.push([lng + dx, lat + dy]);
}
coords.push(coords[0]);
return coords;
};
```
### 5. Build and run
:::warning Rebuild required
Any time you change the `plugins` array in `app.json`, you must create a new native build. A Metro reload or hot reload is not enough.
:::
```bash
# Android emulator or connected device
npx expo run:android
# iOS simulator (macOS only)
npx expo run:ios
# Physical device via EAS
eas build --profile development --platform android
npx expo start
```
---
## Coordinate convention
All coordinates throughout this guide use `[longitude, latitude]` order — the GeoJSON standard:
```typescript
const dhaka: [number, number] = [90.364159, 23.823724]; // ✅ [lng, lat]
const wrong = [23.823724, 90.364159]; // ❌ [lat, lng]
```
---
## Simple map
Render a basic map centered on Dhaka with a single marker. This is the three-step pattern used in every guide: load the style with `useBarikoiMapStyle()`, handle loading/error states, then render `MapView`.
:::tip Marker asset
The example below uses a `` as the marker so it works out of the box without any image assets. Replace it with your own `` when ready.
:::
```tsx
export default function SimpleMapScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
if (loading) {
return (
Loading map...
);
}
if (error) {
return (
Map loading error{error}
);
}
return (
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: BARIKOI_COLORS.background },
statusText: { marginTop: 16, fontSize: 16, color: BARIKOI_COLORS.primary, fontWeight: '500' },
errorTitle: { fontSize: 20, fontWeight: 'bold', color: BARIKOI_COLORS.secondary, marginBottom: 8 },
errorText: { fontSize: 16, color: BARIKOI_COLORS.text, textAlign: 'center' },
marker: { width: 16, height: 16, borderRadius: 8, backgroundColor: BARIKOI_COLORS.primary },
});
```
---
## Utilities reference
All shared utilities live in `utils/mapUtils.ts`:
| Export | Type | Description |
|---|---|---|
| `barikoiClient` | Object | Barikoi API client for search, geocoding, routing |
| `useBarikoiMapStyle()` | Hook | Returns `{ styleJson, loading, error }` |
| `fetchBarikoiMapStyle(apiKey)` | Function | Fetches style JSON from Barikoi |
| `DEFAULT_COORDINATES` | Object | Predefined city coords: `DHAKA`, `CHITTAGONG`, `SYLHET`, `RAJSHAHI` |
| `DEFAULT_CAMERA_SETTINGS` | Object | Default center (Dhaka), zoom (16), animation settings |
| `MAP_STYLES` | Object | Style presets: `line`, `polygon`, `marker` |
| `BARIKOI_COLORS` | Object | Brand colors: `primary`, `secondary`, `background`, `text`, `white` |
| `isWithinBangladeshBounds(coord)` | Function | Returns `true` if `[lng, lat]` is within Bangladesh bounds |
| `calculateDistance(c1, c2)` | Function | Haversine distance in km between two coordinates |
| `createCirclePolygon(center, radiusKm, points?)` | Function | Generates circle polygon coordinates |
### Barikoi API client methods
| Method | Parameters | Returns |
|---|---|---|
| `barikoiClient.autocomplete({ q })` | Search query | `result.data?.places` — array of matching places |
| `barikoiClient.reverseGeocode({ latitude, longitude })` | Coordinates | `result.data?.place` — nearest place object |
| `barikoiClient.nearby({ latitude, longitude, radius, limit })` | Center + radius | `result.data?.places` — array sorted by distance |
| `barikoiClient.routeOverview({ coordinates, geometries })` | Route coords | `result.data?.routes` — array of route objects |
---
## Next steps
- [Markers](./markers) — single, multiple, interactive, and draggable markers
- [Current location](./location) — track and display the user's position
- [Shapes](./shapes) — draw lines, polygons, and circles on the map
- [Search & Geocoding](./search) — integrate Barikoi's search APIs with the map
- [Routing](./routing) — fetch and display routes between two points
- [Troubleshooting](./troubleshooting) — common errors and fixes
---
## Current Location
# Current location
Track the user's position with `expo-location` and display it on the map.
## Permissions setup
Make sure `expo-location` is listed in the `plugins` array in your `app.json` (see [Getting Started](./getting-started)). The plugin automatically adds the required `ACCESS_FINE_LOCATION` permission to `AndroidManifest.xml` and `NSLocationWhenInUseUsageDescription` to `Info.plist` during prebuild.
## Example
```tsx
const convertLocation = (loc: ExpoLocation.LocationObject): Location => ({
coords: {
latitude: loc.coords.latitude,
longitude: loc.coords.longitude,
altitude: loc.coords.altitude ?? undefined,
accuracy: loc.coords.accuracy ?? undefined,
heading: loc.coords.heading ?? undefined,
speed: loc.coords.speed ?? undefined,
},
timestamp: loc.timestamp,
});
export default function CurrentLocationScreen() {
const { styleJson, loading: mapLoading, error: mapError } = useBarikoiMapStyle();
const [userLocation, setUserLocation] = useState(null);
const [hasPermission, setHasPermission] = useState(false);
const [permissionLoading, setPermissionLoading] = useState(true);
const [hasFlownToLocation, setHasFlownToLocation] = useState(false);
const [followsUser, setFollowsUser] = useState(false);
const cameraRef = useRef(null);
useEffect(() => {
let subscription: ExpoLocation.LocationSubscription;
const init = async () => {
try {
const { status } = await ExpoLocation.requestForegroundPermissionsAsync();
if (status === 'granted') {
setHasPermission(true);
const loc = await ExpoLocation.getCurrentPositionAsync({});
setUserLocation(convertLocation(loc));
subscription = await ExpoLocation.watchPositionAsync(
{ accuracy: ExpoLocation.Accuracy.High, timeInterval: 1000, distanceInterval: 10 },
(loc) => setUserLocation(convertLocation(loc)),
);
} else {
Alert.alert('Permission denied', 'Location permission is required.');
}
} finally {
setPermissionLoading(false);
}
};
init();
return () => { if (subscription) subscription.remove(); };
}, []);
useEffect(() => {
if (userLocation && (followsUser || !hasFlownToLocation) && cameraRef.current) {
const { latitude, longitude } = userLocation.coords;
cameraRef.current.setCamera({
centerCoordinate: [longitude, latitude],
zoomLevel: 16,
animationDuration: 2000,
animationMode: 'flyTo',
});
if (!hasFlownToLocation) setHasFlownToLocation(true);
}
}, [userLocation, hasFlownToLocation, followsUser]);
if (mapLoading || permissionLoading) {
return ;
}
if (mapError) {
return {mapError};
}
return (
{hasPermission && (
setUserLocation(loc)}
onPress={() => {
if (userLocation) Alert.alert('Location', `Lat: ${userLocation.coords.latitude}\nLng: ${userLocation.coords.longitude}`);
}}
/>
)}
setFollowsUser(prev => !prev)}
>
◎
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
controls: { position: 'absolute', right: 16, bottom: 32 },
controlBtn: { width: 44, height: 44, borderRadius: 22, backgroundColor: 'white', justifyContent: 'center', alignItems: 'center', elevation: 5, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 3.84 },
controlBtnActive: { backgroundColor: BARIKOI_COLORS.primary },
});
```
## Key points
- Convert between `expo-location` and MapLibre `Location` types with the `convertLocation` helper
- Use `watchPositionAsync` for continuous tracking — not polling
- Always remove the subscription on unmount to avoid memory leaks
- Use `cameraRef` to fly to the user's location on first update
---
## Markers
## Single marker
The `anchor` prop controls where the marker view attaches to the coordinate:
- `{ x: 0.5, y: 1.0 }` — bottom center (pin style)
- `{ x: 0.5, y: 0.5 }` — center (default)
```tsx
```
## Multiple interactive markers with bottom sheet
```tsx
const markers = [
{ id: '1', coordinate: [90.389709, 23.874577] as [number, number], title: 'Uttara', description: 'Modern Township' },
{ id: '2', coordinate: [90.415482, 23.793059] as [number, number], title: 'Gulshan', description: 'Business District' },
{ id: '3', coordinate: [90.367456, 23.747431] as [number, number], title: 'Dhanmondi', description: 'Cultural Hub' },
{ id: '4', coordinate: [90.399452, 23.869585] as [number, number], title: 'Airport', description: 'Hazrat Shahjalal International' },
{ id: '5', coordinate: [90.364159, 23.823724] as [number, number], title: 'Barikoi Head Office', description: 'Main office location' },
];
export default function MarkerScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const [selectedMarker, setSelectedMarker] = useState(null);
const bottomSheetAnim = useRef(new Animated.Value(0)).current;
const showBottomSheet = useCallback(() => {
Animated.spring(bottomSheetAnim, { toValue: 1, useNativeDriver: true }).start();
}, [bottomSheetAnim]);
const hideBottomSheet = useCallback(() => {
Animated.spring(bottomSheetAnim, { toValue: 0, useNativeDriver: true }).start();
setSelectedMarker(null);
}, [bottomSheetAnim]);
const handleMarkerPress = useCallback((markerId: string) => {
setSelectedMarker(markerId);
showBottomSheet();
}, [showBottomSheet]);
if (loading) return ;
if (error) return {error};
const selected = markers.find(m => m.id === selectedMarker);
return (
{markers.map((marker) => (
handleMarkerPress(marker.id)}>
))}
{selected && (
{selected.title}{selected.description}✕Latitude{selected.coordinate[1].toFixed(6)}Longitude{selected.coordinate[0].toFixed(6)}
)}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
markerDot: { width: 16, height: 16, borderRadius: 8, backgroundColor: BARIKOI_COLORS.primary },
markerDotSelected: { width: 20, height: 20, borderRadius: 10, backgroundColor: BARIKOI_COLORS.secondary },
bottomSheet: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: 'white', borderTopLeftRadius: 20, borderTopRightRadius: 20, shadowColor: '#000', shadowOffset: { width: 0, height: -2 }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5, paddingBottom: 20 },
sheetContent: { padding: 16 },
sheetHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 },
sheetTitle: { fontSize: 18, fontWeight: 'bold', color: BARIKOI_COLORS.text, marginBottom: 4 },
sheetDesc: { fontSize: 14, color: BARIKOI_COLORS.text, opacity: 0.7 },
closeBtn: { width: 30, height: 30, alignItems: 'center', justifyContent: 'center', borderRadius: 15, backgroundColor: BARIKOI_COLORS.background },
closeText: { fontSize: 16, color: BARIKOI_COLORS.text },
coordBox: { backgroundColor: BARIKOI_COLORS.background, borderRadius: 12, padding: 16 },
coordLabel: { fontSize: 12, color: BARIKOI_COLORS.text, opacity: 0.7, marginBottom: 4 },
coordValue: { fontSize: 16, color: BARIKOI_COLORS.text, fontWeight: '500' },
});
```
:::tip Performance at scale
`MarkerView` renders native Android/iOS views, which is fine for up to ~50 markers. For 100+ markers, use `ShapeSource` with a `SymbolLayer` instead — it renders markers as map tiles and handles thousands efficiently.
:::
---
## Draggable marker
Let users drag a marker or tap the map to pick a location. Use `PointAnnotation` (not `MarkerView`) for draggable markers.
```tsx
type DragFeature = Feature;
const formatCoord = (v: number) => `${v.toFixed(6)} deg`;
export default function DraggableMarkerScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const cameraRef = useRef(null);
const [markerCoord, setMarkerCoord] = useState<[number, number]>([90.364159, 23.823724]);
const [zoomLevel, setZoomLevel] = useState(16);
const handleDrag = useCallback((payload: DragFeature) => {
const [lng, lat] = payload.geometry.coordinates as number[];
setMarkerCoord([lng, lat]);
}, []);
const handleMapPress = useCallback((payload: Feature) => {
if (payload.geometry?.type === 'Point') {
setMarkerCoord(payload.geometry.coordinates as [number, number]);
}
}, []);
const formatted = useMemo(() => ({
lat: formatCoord(markerCoord[1]),
lng: formatCoord(markerCoord[0]),
}), [markerCoord]);
if (loading) return ;
if (error) return {error};
return (
Latitude{formatted.lat}Longitude{formatted.lng} {
const z = Math.min(zoomLevel + 1, 22);
setZoomLevel(z);
cameraRef.current?.setCamera({ zoomLevel: z, animationDuration: 300 });
}}>
+{Math.round(zoomLevel)}x {
const z = Math.max(zoomLevel - 1, 0);
setZoomLevel(z);
cameraRef.current?.setCamera({ zoomLevel: z, animationDuration: 300 });
}}>
−
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24 },
infoPanel: { position: 'absolute', bottom: 24, left: 16, right: 16, backgroundColor: 'white', borderRadius: 16, padding: 16, elevation: 10, shadowColor: '#000', shadowOpacity: 0.1, shadowOffset: { width: 0, height: 10 }, shadowRadius: 20 },
coordCard: { flex: 1, marginHorizontal: 4, borderWidth: 1, borderColor: '#e1e1e1', borderRadius: 12, padding: 12, backgroundColor: BARIKOI_COLORS.background },
coordLabel: { fontSize: 12, color: BARIKOI_COLORS.text, opacity: 0.7 },
coordValue: { fontSize: 16, fontWeight: '600', color: BARIKOI_COLORS.text },
zoomControls: { position: 'absolute', right: 16, top: 40, backgroundColor: 'white', borderRadius: 8, elevation: 5, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 3.84 },
zoomBtn: { width: 44, height: 44, justifyContent: 'center', alignItems: 'center' },
zoomText: { fontSize: 22, color: BARIKOI_COLORS.primary, lineHeight: 26 },
zoomLabel: { paddingVertical: 8, alignItems: 'center', borderTopWidth: 1, borderBottomWidth: 1, borderColor: '#e1e1e1' },
});
```
Key points:
- Use `PointAnnotation` (not `MarkerView`) for draggable markers
- Set the `draggable` prop and handle both `onDragStart` and `onDragEnd`
- Handle `onPress` on `MapView` to move the marker on tap
- The drag callback receives a GeoJSON `Feature` — coordinates are `[lng, lat]`
---
## Routing
Fetch and display a route between two points using the `barikoiClient.routeOverview()` method. This combines the [Shapes](./shapes) pattern with a real API call.
## Example
```tsx
const ORIGIN: [number, number] = [90.364159, 23.823724]; // Barikoi Head Office
const DESTINATION: [number, number] = [90.415482, 23.793059]; // Gulshan
type RouteInfo = {
distanceKm: number;
durationMin: number;
geometry: FeatureCollection;
};
export default function RoutingScreen() {
const { styleJson, loading: mapLoading, error: mapError } = useBarikoiMapStyle();
const [route, setRoute] = useState(null);
const [loadingRoute, setLoadingRoute] = useState(false);
const [error, setError] = useState(null);
const fetchRoute = useCallback(async () => {
try {
setLoadingRoute(true);
setError(null);
const coordinates = `${ORIGIN[0]},${ORIGIN[1]};${DESTINATION[0]},${DESTINATION[1]}`;
const result = await barikoiClient.routeOverview({
coordinates,
geometries: 'geojson',
profile: 'car',
});
const routes = result.data?.routes;
if (routes && routes.length > 0) {
const r = routes[0];
const routeGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
properties: {},
geometry: r.geometry as any, // GeoJSON LineString
}],
};
setRoute({
distanceKm: (r.distance ?? 0) / 1000,
durationMin: (r.duration ?? 0) / 60,
geometry: routeGeoJSON,
});
} else {
setError('No route found');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch route');
} finally {
setLoadingRoute(false);
}
}, []);
useEffect(() => { fetchRoute(); }, [fetchRoute]);
if (mapLoading) return ;
if (mapError) return {mapError};
const midPoint: [number, number] = [
(ORIGIN[0] + DESTINATION[0]) / 2,
(ORIGIN[1] + DESTINATION[1]) / 2,
];
return (
{/* Route line */}
{route && (
)}
{/* Origin marker */}
A
{/* Destination marker */}
B
{/* Route info panel */}
{loadingRoute && (
Fetching route...
)}
{error && (
{error}Retry
)}
{route && !loadingRoute && (
{route.distanceKm.toFixed(1)} kmDistance{Math.round(route.durationMin)} minDuration
)}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
originMarker: { width: 28, height: 28, borderRadius: 14, backgroundColor: BARIKOI_COLORS.primary, justifyContent: 'center', alignItems: 'center', borderWidth: 2, borderColor: 'white' },
destMarker: { width: 28, height: 28, borderRadius: 14, backgroundColor: BARIKOI_COLORS.secondary, justifyContent: 'center', alignItems: 'center', borderWidth: 2, borderColor: 'white' },
markerLabel: { color: 'white', fontSize: 13, fontWeight: 'bold' },
infoPanel: { position: 'absolute', bottom: 24, left: 16, right: 16, backgroundColor: 'white', borderRadius: 16, padding: 16, elevation: 10, shadowColor: '#000', shadowOpacity: 0.1, shadowOffset: { width: 0, height: 10 }, shadowRadius: 20 },
loadingRow: { flexDirection: 'row', alignItems: 'center', gap: 10 },
loadingText: { fontSize: 15, color: BARIKOI_COLORS.text, marginLeft: 10 },
errorText: { fontSize: 14, color: BARIKOI_COLORS.secondary, marginBottom: 8 },
retryBtn: { backgroundColor: BARIKOI_COLORS.primary, borderRadius: 8, paddingVertical: 8, alignItems: 'center' },
retryText: { color: 'white', fontWeight: '600' },
routeInfo: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center' },
routeStat: { flex: 1, alignItems: 'center' },
statValue: { fontSize: 20, fontWeight: 'bold', color: BARIKOI_COLORS.text },
statLabel: { fontSize: 12, color: BARIKOI_COLORS.text, opacity: 0.6, marginTop: 2 },
statDivider: { width: 1, height: 30, backgroundColor: '#e0e0e0' },
});
```
## Key points
- The `coordinates` parameter is a string: `lng,lat;lng,lat` (semicolon-separated, GeoJSON order)
- Use `geometries: 'geojson'` to get a GeoJSON LineString you can pass directly to `ShapeSource`
- `distance` is in meters, `duration` is in seconds — convert for display
- The response can include multiple routes; use `routes[0]` for the recommended one
## `routeOverview` parameters
| Parameter | Type | Description |
|---|---|---|
| `coordinates` | string | `lng,lat;lng,lat` (semicolon-separated) |
| `geometries` | `"geojson"` \| `"polyline"` \| `"polyline6"` | Geometry format |
| `profile` | `"car"` \| `"foot"` | Routing profile (default: car) |
---
## Search & Geocoding
Integrate Barikoi's location APIs with the map. The `barikoiapis` SDK provides typed methods for autocomplete search, reverse geocoding, and nearby place queries.
All examples below use the `barikoiClient` initialized in `mapUtils.ts` (see [Getting Started](./getting-started)).
---
## Autocomplete search
Search for places in Bangladesh and display results as markers on the map.
```tsx
ActivityIndicator,
Animated,
FlatList,
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
type Place = {
id: number;
longitude: string | number;
latitude: string | number;
address: string;
area: string;
city: string;
postCode: string | number;
};
export default function AutocompleteSearchScreen() {
const { styleJson, loading: mapLoading, error: mapError } = useBarikoiMapStyle();
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [searching, setSearching] = useState(false);
const [selectedPlace, setSelectedPlace] = useState(null);
const bottomSheetAnim = useRef(new Animated.Value(0)).current;
const cameraRef = useRef(null);
const debounceRef = useRef>();
// Debounced search using barikoiapis SDK
useEffect(() => {
if (!query.trim()) {
setResults([]);
return;
}
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(async () => {
try {
setSearching(true);
const result = await barikoiClient.autocomplete({ q: query });
setResults((result.data?.places || []) as Place[]);
} catch {
setResults([]);
} finally {
setSearching(false);
}
}, 400);
return () => { if (debounceRef.current) clearTimeout(debounceRef.current); };
}, [query]);
const showBottomSheet = useCallback(() => {
Animated.spring(bottomSheetAnim, { toValue: 1, useNativeDriver: true }).start();
}, [bottomSheetAnim]);
const hideBottomSheet = useCallback(() => {
Animated.spring(bottomSheetAnim, { toValue: 0, useNativeDriver: true }).start();
setSelectedPlace(null);
}, [bottomSheetAnim]);
const selectPlace = useCallback((place: Place) => {
setSelectedPlace(place);
setResults([]);
setQuery(place.address);
showBottomSheet();
cameraRef.current?.setCamera({
centerCoordinate: [Number(place.longitude), Number(place.latitude)],
zoomLevel: 16,
animationDuration: 1000,
animationMode: 'flyTo',
});
}, [showBottomSheet]);
if (mapLoading) return ;
if (mapError) return {mapError};
return (
{selectedPlace && (
)}
{results.map((place) => (
))}
{/* Search bar */}
{searching && }
{/* Search results dropdown */}
{results.length > 0 && (
String(item.id)}
renderItem={({ item }) => (
selectPlace(item)}>
{item.address}{item.area}, {item.city}
)}
keyboardShouldPersistTaps="handled"
/>
)}
{/* Selected place bottom sheet */}
{selectedPlace && (
{selectedPlace.address}{selectedPlace.area}, {selectedPlace.city} {selectedPlace.postCode}✕Latitude{Number(selectedPlace.latitude).toFixed(6)}Longitude{Number(selectedPlace.longitude).toFixed(6)}
)}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
selectedMarker: { width: 20, height: 20, borderRadius: 10, backgroundColor: BARIKOI_COLORS.secondary, borderWidth: 3, borderColor: 'white' },
resultMarker: { width: 12, height: 12, borderRadius: 6, backgroundColor: BARIKOI_COLORS.primary },
searchContainer: { position: 'absolute', top: 16, left: 16, right: 16, backgroundColor: 'white', borderRadius: 12, elevation: 5, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 3.84 },
searchInput: { padding: 14, fontSize: 16, color: BARIKOI_COLORS.text },
searchSpinner: { position: 'absolute', right: 16, top: 18 },
resultsList: { position: 'absolute', top: 70, left: 16, right: 16, maxHeight: 240, backgroundColor: 'white', borderRadius: 12, elevation: 5, shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.25, shadowRadius: 3.84 },
resultItem: { padding: 14, borderBottomWidth: 1, borderBottomColor: '#f0f0f0' },
resultAddress: { fontSize: 15, fontWeight: '500', color: BARIKOI_COLORS.text },
resultArea: { fontSize: 13, color: BARIKOI_COLORS.text, opacity: 0.6, marginTop: 2 },
bottomSheet: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: 'white', borderTopLeftRadius: 20, borderTopRightRadius: 20, shadowColor: '#000', shadowOffset: { width: 0, height: -2 }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5, paddingBottom: 20 },
sheetContent: { padding: 16 },
sheetHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 },
sheetTitle: { fontSize: 18, fontWeight: 'bold', color: BARIKOI_COLORS.text, marginBottom: 4 },
sheetDesc: { fontSize: 14, color: BARIKOI_COLORS.text, opacity: 0.7 },
closeBtn: { width: 30, height: 30, alignItems: 'center', justifyContent: 'center', borderRadius: 15, backgroundColor: BARIKOI_COLORS.background },
closeText: { fontSize: 16, color: BARIKOI_COLORS.text },
coordBox: { backgroundColor: BARIKOI_COLORS.background, borderRadius: 12, padding: 16 },
coordLabel: { fontSize: 12, color: BARIKOI_COLORS.text, opacity: 0.7, marginBottom: 4 },
coordValue: { fontSize: 16, color: BARIKOI_COLORS.text, fontWeight: '500' },
});
```
Key points:
- **Debounce the input** — 400ms delay avoids hammering the API on every keystroke
- `barikoiClient.autocomplete({ q })` returns `result.data?.places` as an array
- Results include `longitude` and `latitude` (may be `string | number` — use `Number()`)
- Use `cameraRef.current.setCamera()` with `animationMode: 'flyTo'` to animate to the selected place
Optional autocomplete parameters:
| Parameter | Type | Description |
|---|---|---|
| `bangla` | boolean | Include Bangla names in the response |
---
## Reverse geocoding
Tap the map to get the address at that coordinate.
```tsx
type ReverseGeoResult = {
address: string;
address_bn: string;
area: string;
city: string;
postCode: string;
subType: string;
thana: string;
division: string;
distance_within_meters: number;
};
export default function ReverseGeocodingScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const cameraRef = useRef(null);
const [markerCoord, setMarkerCoord] = useState<[number, number]>([90.364159, 23.823724]);
const [addressResult, setAddressResult] = useState(null);
const [loadingAddress, setLoadingAddress] = useState(false);
const reverseGeocode = useCallback(async (lng: number, lat: number) => {
try {
setLoadingAddress(true);
const result = await barikoiClient.reverseGeocode({ longitude: lng, latitude: lat });
const place = result.data?.place;
if (place) {
setAddressResult({
address: place.address || '',
address_bn: place.address_bn || '',
area: place.area || '',
city: place.city || '',
postCode: place.postCode || '',
subType: place.subType || '',
thana: place.thana || '',
division: place.division || '',
distance_within_meters: place.distance_within_meters ?? 0,
});
} else {
setAddressResult(null);
}
} catch {
setAddressResult(null);
} finally {
setLoadingAddress(false);
}
}, []);
const handleMapPress = useCallback((payload: Feature) => {
if (payload.geometry?.type === 'Point') {
const [lng, lat] = payload.geometry.coordinates as [number, number];
setMarkerCoord([lng, lat]);
reverseGeocode(lng, lat);
}
}, [reverseGeocode]);
if (loading) return ;
if (error) return {error};
return (
{/* Address card */}
{loadingAddress ? (
Looking up address...
) : addressResult ? (
{addressResult.address}{addressResult.area}, {addressResult.city} {addressResult.postCode}Lat{markerCoord[1].toFixed(6)}Lng{markerCoord[0].toFixed(6)}
) : (
Tap the map to look up an address
)}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
marker: { width: 24, height: 24, borderRadius: 12, backgroundColor: BARIKOI_COLORS.secondary, borderWidth: 3, borderColor: 'white' },
addressCard: { position: 'absolute', bottom: 24, left: 16, right: 16, backgroundColor: 'white', borderRadius: 16, padding: 16, elevation: 10, shadowColor: '#000', shadowOpacity: 0.1, shadowOffset: { width: 0, height: 10 }, shadowRadius: 20 },
loadingRow: { flexDirection: 'row', alignItems: 'center', gap: 10 },
loadingText: { fontSize: 15, color: BARIKOI_COLORS.text, marginLeft: 10 },
addressTitle: { fontSize: 17, fontWeight: '600', color: BARIKOI_COLORS.text, marginBottom: 4 },
addressDetail: { fontSize: 14, color: BARIKOI_COLORS.text, opacity: 0.7, marginBottom: 12 },
coordRow: { flexDirection: 'row', gap: 12 },
coordItem: { flex: 1, backgroundColor: BARIKOI_COLORS.background, borderRadius: 10, padding: 10 },
coordLabel: { fontSize: 11, color: BARIKOI_COLORS.text, opacity: 0.6, marginBottom: 2 },
coordValue: { fontSize: 14, fontWeight: '500', color: BARIKOI_COLORS.text },
tapPrompt: { fontSize: 15, color: BARIKOI_COLORS.text, opacity: 0.5, textAlign: 'center' },
});
```
Key points:
- `barikoiClient.reverseGeocode({ latitude, longitude })` returns `result.data?.place` — a single object (not an array)
- Check `distance_within_meters` to gauge how accurate the result is
- Use `PointAnnotation` for the marker so users can see the exact tapped position
Optional reverse geocode parameters:
| Parameter | Type | Description |
|---|---|---|
| `district` | boolean | Include district data |
| `bangla` | boolean | Include Bangla names |
| `thana` | boolean | Include thana/upazila data |
| `division` | boolean | Include division data |
:::caution Extra credits
Each enabled optional parameter triggers additional API calls and consumes more credits.
:::
---
## Nearby places
Find nearby points of interest using the user's current location.
```tsx
type NearbyPlace = {
id: number;
name: string;
longitude: string;
latitude: string;
Address: string;
area: string;
city: string;
pType: string;
subType: string;
uCode: string;
distance_in_meters: string | number;
};
export default function NearbyPlacesScreen() {
const { styleJson, loading: mapLoading, error: mapError } = useBarikoiMapStyle();
const [userCoord, setUserCoord] = useState<[number, number]>([90.364159, 23.823724]);
const [places, setPlaces] = useState([]);
const [loadingPlaces, setLoadingPlaces] = useState(false);
const [selectedPlace, setSelectedPlace] = useState(null);
const bottomSheetAnim = useRef(new Animated.Value(0)).current;
const cameraRef = useRef(null);
// Get user location
useEffect(() => {
(async () => {
const { status } = await ExpoLocation.requestForegroundPermissionsAsync();
if (status === 'granted') {
const loc = await ExpoLocation.getCurrentPositionAsync({});
setUserCoord([loc.coords.longitude, loc.coords.latitude]);
}
})();
}, []);
// Fetch nearby places using barikoiapis SDK
const fetchNearby = useCallback(async (lng: number, lat: number) => {
try {
setLoadingPlaces(true);
const result = await barikoiClient.nearby({
longitude: lng,
latitude: lat,
radius: 1, // 1km
limit: 20,
});
setPlaces((result.data?.places || []) as NearbyPlace[]);
} catch {
setPlaces([]);
} finally {
setLoadingPlaces(false);
}
}, []);
useEffect(() => {
fetchNearby(userCoord[0], userCoord[1]);
}, [userCoord, fetchNearby]);
const showBottomSheet = useCallback(() => {
Animated.spring(bottomSheetAnim, { toValue: 1, useNativeDriver: true }).start();
}, [bottomSheetAnim]);
const hideBottomSheet = useCallback(() => {
Animated.spring(bottomSheetAnim, { toValue: 0, useNativeDriver: true }).start();
setSelectedPlace(null);
}, [bottomSheetAnim]);
const selectPlace = useCallback((place: NearbyPlace) => {
setSelectedPlace(place);
showBottomSheet();
cameraRef.current?.setCamera({
centerCoordinate: [Number(place.longitude), Number(place.latitude)],
zoomLevel: 16,
animationDuration: 800,
animationMode: 'flyTo',
});
}, [showBottomSheet]);
if (mapLoading) return ;
if (mapError) return {mapError};
return (
{/* User location marker */}
{/* Nearby place markers */}
{places.map((place) => (
selectPlace(place)}>
{Math.round(Number(place.distance_in_meters))}m
))}
{/* Places list */}
Nearby Places
{loadingPlaces && }
String(item.id)}
horizontal
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => (
selectPlace(item)}
>
{item.name || item.Address}{item.pType}{Math.round(Number(item.distance_in_meters))}m away
)}
/>
{/* Selected place bottom sheet */}
{selectedPlace && (
{selectedPlace.name || selectedPlace.Address}{selectedPlace.area}, {selectedPlace.city}✕Type{selectedPlace.pType}Distance{Math.round(Number(selectedPlace.distance_in_meters))}mArea{selectedPlace.area}
)}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
userDot: { width: 16, height: 16, borderRadius: 8, backgroundColor: '#4285F4', borderWidth: 3, borderColor: 'white' },
placeMarker: { backgroundColor: BARIKOI_COLORS.primary, borderRadius: 12, paddingHorizontal: 8, paddingVertical: 4, minWidth: 40, alignItems: 'center' },
placeMarkerSelected: { backgroundColor: BARIKOI_COLORS.secondary },
placeMarkerText: { color: 'white', fontSize: 11, fontWeight: '600' },
listContainer: { position: 'absolute', bottom: 16, left: 0, right: 0 },
listHeader: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, marginBottom: 8, gap: 8 },
listTitle: { fontSize: 16, fontWeight: '600', color: BARIKOI_COLORS.text },
placeCard: { backgroundColor: 'white', borderRadius: 12, padding: 12, marginRight: 8, width: 160, elevation: 3, shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 4 },
placeCardSelected: { borderWidth: 2, borderColor: BARIKOI_COLORS.primary },
placeName: { fontSize: 14, fontWeight: '600', color: BARIKOI_COLORS.text, marginBottom: 2 },
placeType: { fontSize: 12, color: BARIKOI_COLORS.primary, marginBottom: 4 },
placeDistance: { fontSize: 12, color: BARIKOI_COLORS.text, opacity: 0.6 },
bottomSheet: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: 'white', borderTopLeftRadius: 20, borderTopRightRadius: 20, shadowColor: '#000', shadowOffset: { width: 0, height: -2 }, shadowOpacity: 0.25, shadowRadius: 3.84, elevation: 5, paddingBottom: 20 },
sheetContent: { padding: 16 },
sheetHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 16 },
sheetTitle: { fontSize: 18, fontWeight: 'bold', color: BARIKOI_COLORS.text, marginBottom: 4 },
sheetDesc: { fontSize: 14, color: BARIKOI_COLORS.text, opacity: 0.7 },
closeBtn: { width: 30, height: 30, alignItems: 'center', justifyContent: 'center', borderRadius: 15, backgroundColor: BARIKOI_COLORS.background },
closeText: { fontSize: 16, color: BARIKOI_COLORS.text },
detailRow: { flexDirection: 'row', gap: 8 },
detailChip: { flex: 1, backgroundColor: BARIKOI_COLORS.background, borderRadius: 10, padding: 10 },
detailLabel: { fontSize: 11, color: BARIKOI_COLORS.text, opacity: 0.6, marginBottom: 2 },
detailValue: { fontSize: 14, fontWeight: '500', color: BARIKOI_COLORS.text },
});
```
Key points:
- `barikoiClient.nearby({ latitude, longitude, radius, limit })` returns `result.data?.places` sorted by distance
- `radius` is in kilometers, `limit` caps the result count
- Each result includes `distance_in_meters` and `pType` (place category)
- Response fields like `longitude`, `latitude`, `distance_in_meters` may be `string | number` — use `Number()`
---
## Shapes
## Drawing lines
Draw routes and paths using GeoJSON `LineString`.
```tsx
export default function LineScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const lineGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
properties: {},
geometry: {
type: 'LineString',
coordinates: [
[90.364159, 23.823724],
[90.369159, 23.825724],
],
},
}],
};
if (loading) return ;
if (error) return {error};
return (
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
dot: { width: 14, height: 14, borderRadius: 7, backgroundColor: BARIKOI_COLORS.primary },
});
```
Line style properties:
| Property | Type | Description |
|---|---|---|
| `lineColor` | string | Color (e.g. `'#2e8555'`) |
| `lineWidth` | number | Width in pixels |
| `lineCap` | `"butt"` \| `"round"` \| `"square"` | End cap style |
| `lineJoin` | `"bevel"` \| `"round"` \| `"miter"` | Join style |
| `lineDasharray` | number[] | e.g. `[2, 2]` for dashed lines |
---
## Drawing polygons
Render filled areas using GeoJSON `Polygon`.
:::caution Close the ring
The first and last coordinate of a polygon ring must be identical. If they don't match, the polygon will not render.
:::
```tsx
export default function PolygonScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const polygonGeoJSON = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [[
[90.364159, 23.823724],
[90.369159, 23.825724],
[90.367159, 23.820724],
[90.364159, 23.823724], // must equal first point
]],
},
}],
};
const vertices = [[90.364159, 23.823724], [90.369159, 23.825724], [90.367159, 23.820724]];
if (loading) return ;
if (error) return {error};
return (
{/* Add a LineLayer for a thicker outline */}
{vertices.map((coord, i) => (
))}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
dot: { width: 14, height: 14, borderRadius: 7, backgroundColor: BARIKOI_COLORS.primary },
});
```
Polygon fill properties:
| Property | Type | Description |
|---|---|---|
| `fillColor` | string | Fill color (e.g. `'rgba(46, 133, 85, 0.5)'`) |
| `fillOutlineColor` | string | Border color |
| `fillOpacity` | number | Opacity 0–1 |
For a thicker outline, add a `LineLayer` after the `FillLayer` inside the same `ShapeSource`.
---
## Circle polygons
MapLibre does not have a built-in circle shape. Use the `createCirclePolygon` utility from `mapUtils.ts` to approximate one with a many-sided polygon.
```tsx
const circleCoords = createCirclePolygon([90.364159, 23.823724], 0.5); // 500m radius
const circleGeoJSON = {
type: 'FeatureCollection',
features: [{
type: 'Feature',
geometry: {
type: 'Polygon',
coordinates: [circleCoords], // wrap in array for the ring
},
}],
};
// Render with FillLayer
```
`createCirclePolygon` parameters:
| Parameter | Type | Description |
|---|---|---|
| `center` | `[number, number]` | `[longitude, latitude]` |
| `radiusInKm` | number | Radius in kilometers (e.g. `0.5` for 500m) |
| `points` | number | Vertex count, default `64` (higher = smoother) |
---
## Combined geometry
Combine circles, lines, polygons, and markers on a single map. Each shape type uses its own `ShapeSource` and layer.
:::warning Unique IDs required
Every `ShapeSource` and layer must have a unique `id` across the entire map. Duplicate IDs will cause rendering errors.
:::
```tsx
export default function GeometryScreen() {
const { styleJson, loading, error } = useBarikoiMapStyle();
const center: [number, number] = [90.366659, 23.823724];
const circleGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{ type: 'Feature', properties: {}, geometry: { type: 'Polygon', coordinates: [createCirclePolygon(center, 0.3)] } }],
};
const lineGeoJSON: FeatureCollection = {
type: 'FeatureCollection',
features: [{ type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: [[90.364159, 23.823724], [90.369159, 23.825724]] } }],
};
const markerPoints = [[90.364159, 23.823724], [90.369159, 23.825724], center];
if (loading) return ;
if (error) return {error};
return (
{markerPoints.map((coord, i) => (
))}
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: 'white' },
map: { flex: 1 },
centered: { flex: 1, justifyContent: 'center', alignItems: 'center' },
dot: { width: 14, height: 14, borderRadius: 7, backgroundColor: BARIKOI_COLORS.primary },
});
```
---
## Troubleshooting
Common issues and their fixes when working with Barikoi Maps in React Native.
## Map shows a blank white screen
**Cause:** The map style URL could not be loaded — typically a missing or invalid API key.
**Fix:**
1. Check that `extra.barikoiApiKey` is set in your `app.json`
2. Verify the key is valid at [developer.barikoi.com](https://developer.barikoi.com) → Dashboard → Account → API Key
3. Check the console for HTTP 403 errors from `map.barikoi.com`
4. Make sure you passed `mapStyle={styleJson}` (not `mapStyle="url"`) to `MapView` — the `useBarikoiMapStyle` hook fetches and returns the full JSON object
## Build fails after adding `@maplibre/maplibre-react-native`
**Cause:** The native module was not linked. This happens when you install the package but don't rebuild the native project.
**Fix:**
```bash
npx expo prebuild --clean
npx expo run:android # or: npx expo run:ios
```
:::warning
`npx expo start` alone is not enough after adding or changing native modules. You must create a new native build.
:::
## "Native module not found" error in Expo Go
**Cause:** `@maplibre/maplibre-react-native` uses native code that Expo Go does not support.
**Fix:** Use a development build instead of Expo Go:
```bash
npm install expo-dev-client
# Add "expo-dev-client" to your plugins array in app.json
npx expo run:android # or: npx expo run:ios
```
## Location permission denied on Android
**Cause:** The `expo-location` config plugin was not registered, so the `ACCESS_FINE_LOCATION` permission was not added to `AndroidManifest.xml`.
**Fix:** Add `expo-location` to the `plugins` array in `app.json`:
```json
{
"expo": {
"plugins": [
"expo-location",
"@maplibre/maplibre-react-native"
]
}
}
```
Then rebuild: `npx expo prebuild --clean && npx expo run:android`
## Style JSON fetch returns 403 Forbidden
**Cause:** The API key is invalid, expired, or does not have access to the map tiles API.
**Fix:**
1. Verify the key at [developer.barikoi.com](https://developer.barikoi.com)
2. Check that the key is for the correct environment (production vs. development)
3. Ensure the key is being read correctly from `Constants.expoConfig?.extra?.barikoiApiKey`
## Polygon renders but appears as a triangle or line
**Cause:** The polygon ring is not closed — the first and last coordinates do not match.
**Fix:** Ensure the last coordinate equals the first:
```typescript
coordinates: [
[90.364, 23.823],
[90.369, 23.825],
[90.367, 23.820],
[90.364, 23.823], // must equal the first point
]
```
Or use `createCirclePolygon()` from `mapUtils.ts` which automatically closes the ring.
## Markers flicker or disappear when zooming
**Cause:** `MarkerView` re-renders its React view on every frame change, causing performance issues with many markers.
**Fix:**
- For fewer than 50 markers, wrap the marker content in `React.memo`
- For 50+ markers, switch from `MarkerView` to `ShapeSource` with a `SymbolLayer` which renders markers as map tiles
## App crashes on launch after changing `app.json` plugins
**Cause:** The native build is stale — `app.json` plugin changes require a full rebuild, but the cached build doesn't include the new native code.
**Fix:**
```bash
# Remove the generated native projects
rm -rf android ios
# Regenerate with the new plugins
npx expo prebuild
# Rebuild
npx expo run:android # or: npx expo run:ios
```
## API requests return empty results
**Cause:** The search query might not match any places, or the coordinates might be outside Bangladesh.
**Fix:**
1. Test the API directly in a browser: `https://barikoi.xyz/v2/api/search/autocomplete/place?api_key=YOUR_KEY&q=dhaka`
2. Verify coordinates are in `[longitude, latitude]` order (not `[lat, lng]`)
3. Use `isWithinBangladeshBounds([lng, lat])` to validate coordinates before making API calls
---
## Add a Line on Map
To add a line on map, check out the following code
```dart
class LineMap extends StatefulWidget{
@override
State createState() => _LineMapState();
}
class _LineMapState extends State {
CameraPosition initialPosition = CameraPosition(
target: LatLng(23.835677, 90.380325),
zoom: 12); //CameraPosition object for initial location in map
MaplibreMapController? mController;
static const styleId = 'osm-liberty'; //barikoi map style id
static const apiKey = BARIKOI_API_KEY; //barikoi API key, get it from https://developer.barikoi.com
static const mapUrl = 'https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey';
@override
Widget build(BuildContext context) {
return Scaffold(
body: MaplibreMap(
initialCameraPosition: initialPosition,
// set map initial location where map will show first
onMapCreated: (
MaplibreMapController mapController) { //called when map object is created
mController =
mapController; // use the MaplibreMapController for map operations
},
styleString: mapUrl, // barikoi map style url
onStyleLoadedCallback: (){
mController?.addLine(
const LineOptions(
geometry: [ //geometry of the line , in List> format
LatLng(23.87397849117633,90.4004025152986),
LatLng(23.860512893207584,90.4004025152986),
LatLng(23.837857327354314,90.41815482211757),
LatLng(23.82212196615106,90.42035665862238),
LatLng(23.812428033815493,90.40370527005587),
LatLng(23.762436156214207,90.39462262442248),
],
lineColor: "#ff0000", //color of the line, in hex string
lineWidth: 5.0, //width of the line
lineOpacity: 0.5, // transparency of the line
draggable: true //set whether line is dragabble
)
);
//add line tap listner
mController?.onLineTapped.add(_OnLineTapped);
},
),
);
}
void _OnLineTapped(Line line) {
//implement line tap event here
}
}
```
---
## Add a Polygon
To add a polygon on map, check out this code
```dart
class FillMap extends StatefulWidget{
@override
State createState() => _FillMapState();
}
class _FillMapState extends State{
CameraPosition initialPosition= CameraPosition(target: LatLng(23.835677, 90.380325), zoom: 12); //CameraPosition object for initial location in map
MaplibreMapController? mController;
static const styleId = 'osm-liberty' ; //barikoi map style id
static const apiKey=BARIKOI_API_KEY; //barikoi API key, get it from https://developer.barikoi.com
static const mapUrl= 'https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey';
@override
Widget build(BuildContext context) {
return Scaffold(
body: MaplibreMap(
initialCameraPosition: initialPosition, // set map initial location where map will show first
onMapCreated: (MaplibreMapController mapController){ //called when map object is created
mController= mapController; // use the MaplibreMapController for map operations
},
styleString: mapUrl , // barikoi map style url
onStyleLoadedCallback: (){
//add polygon to map
mController?.addFill(
const FillOptions(
geometry: [ //geometry of the polygon , in >> format
[
LatLng(23.85574361143307, 90.38354443076582),
LatLng(23.823632508626005,90.40521296373265),
LatLng(23.82639837105691,90.42285014172887),
LatLng(23.86204198543561,90.40050971626783)
]
],
fillColor: "#FF0000",
fillOutlineColor: "#FFFFFF",
fillOpacity: 0.5,
draggable: true
),
);
//add Fill tap event listener
mController?.onFillTapped.add(_OnFillTapped);
}
),
);
}
void _OnFillTapped(Fill argument) {
// implement polygon fill tap event here
}
}
```
---
## Add a Simple Map
You can add asimple map using the MaplibreMap widget. Set the style URL and API key in the widget from barikoi developer dashboard. Check out this sample code :
```dart
class SimpleMap extends StatefulWidget{
@override
State createState() => _SimpleMapState();
}
class _SimpleMapState extends State{
CameraPosition initialPosition= CameraPosition(target: LatLng(23.835677, 90.380325), zoom: 12); //CameraPosition object for initial location in map
MaplibreMapController? mController;
static const styleId = 'osm-liberty' ; //barikoi map style id
static const apiKey = BARIKOI_API_KEY; //barikoi API key, get it from https://developer.barikoi.com
static const mapUrl= 'https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey';
@override
Widget build(BuildContext context) {
return Scaffold(
body: MaplibreMap(
initialCameraPosition: initialPosition, // set map initial location where map will show first
onMapCreated: (MaplibreMapController mapController){ //called when map object is created
mController= mapController; // use the MaplibreMapController for map operations
},
styleString: mapUrl , // barikoi map style url
),
);
}
}
```
---
## Add a Symbol
Check out the following code to add symbol in your map
```dart
class SymbolMap extends StatefulWidget{
@override
State createState() => _SymbolMapState();
}
class _SymbolMapState extends State{
CameraPosition initialPosition= CameraPosition(target: LatLng(23.835677, 90.380325), zoom: 12);
MaplibreMapController? mController;
static const styleId = 'osm-liberty' ; //barikoi map style id
static const apiKey=BARIKOI_API_KEY; //barikoi API key, get it from https://developer.barikoi.com
static const mapUrl= 'https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey';
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaplibreMap(
initialCameraPosition: initialPosition,
styleString: mapUrl,
onMapCreated: (MaplibreMapController mapController){ //called when map object is created
mController= mapController; // use the MaplibreMapController for map operations
mController?.onSymbolTapped.add(_OnSymboltapped); // add symbol tap event listener to mapcontroller
},
onStyleLoadedCallback: (){
// Create SymbolOption for creating a symbol in map
SymbolOptions symbolOptions= const SymbolOptions(
geometry: LatLng(23.835677, 90.380325), // location of the symbol, required
iconImage: 'custom-marker', // icon image of the symbol
//optional parameter to configure the symbol
iconSize: .4, // size of the icon in ratio of the actual size, optional
iconAnchor: 'bottom', // anchor direction of the icon on the location specified, optional
textField: 'test', // Text to show on the symbol, optional
textSize: 12.5,
textOffset: Offset(0, 1.2), // shifting the text position relative to the symbol with x,y axis value, optional
textAnchor: 'bottom', // anchor direction of the text on the location specified, optional
textColor: '#000000',
textHaloBlur: 1,
textHaloColor: '#ffffff',
textHaloWidth: 0.8,
);
addImageFromAsset("custom-marker", "assets/marker.png").then((value) { mController?.addSymbol(symbolOptions);});
},
);
}
_OnSymboltapped(Symbol symbol){
//update symbol text when tapped
mController?.updateSymbol(symbol, SymbolOptions(textField: "clicked"));
}
// Adds an asset image to the currently displayed style
Future addImageFromAsset(String name, String assetName) async {
final ByteData bytes = await rootBundle.load(assetName);
final Uint8List list = bytes.buffer.asUint8List();
return mController!.addImage(name, list);
}
// Adds a network image to the currently displayed style
Future addImageFromUrl(String name, Uri uri) async {
var response = await http.get(uri);
return mController!.addImage(name, response.bodyBytes);
}
}
```
---
## Camera Controls
To control map camera position and camera animation , check out this code
```dart
class SimpleMap extends StatefulWidget{
@override
State createState() => _SimpleMapState();
}
class _SimpleMapState extends State{
CameraPosition initialPosition= CameraPosition(target: LatLng(23.835677, 90.380325), zoom: 12); //CameraPosition object for initial location in map
MaplibreMapController? mController;
static const styleId = 'osm-liberty' ; //barikoi map style id
static const apiKey=BARIKOI_API_KEY; //barikoi API key, get it from https://developer.barikoi.com
static const mapUrl= 'https://map.barikoi.com/styles/$styleId/style.json?key=$apiKey';
@override
Widget build(BuildContext context) {
return Scaffold(
body: MaplibreMap(
initialCameraPosition: initialPosition, // set map initial location where map will show first
onMapCreated: (MaplibreMapController mapController){ //called when map object is created
mController= mapController; // use the MaplibreMapController for map operations
},
styleString: mapUrl , // barikoi map style url
onStyleLoadedCallback: (){
//implement any of the funtions below to test different kind of camera controls, here newCameraPosition function is implemented
_setCameraPosition(mController)
}
),
);
}
}
...
void _setCameraPosition(MaplibreMapController? mController){
//set new CameraPostion for map
mController?.animateCamera(
CameraUpdate.newCameraPosition(
const CameraPosition(
bearing: 270.0, //bearing of the map view, value range 0- 360
target: LatLng(23.3160895, 90.81294527), // LatLng position of the location
tilt: 30.0, // tilt the map by degree
zoom: 17.0, // zoom level of the map
),
)
);
}
void _setNewLatlng(MaplibreMapController? mController){
//animate camera to the Latlng position
mController?.animateCamera(
CameraUpdate.newLatLng(
const LatLng(23.824775, 90.360954), // LatLng position of the location
),
duration: const Duration(seconds: 3), // map camera animation duration
).then((result) => debugPrint("mController?.animateCamera() returned $result"));
}
void _setNewLatlngZoom( MaplibreMapController? mController){
//animate camera to the Latlng position with zoom level
mController?.animateCamera(
CameraUpdate.newLatLngZoom(const LatLng(23.774506, 90.444063), 7),
duration: const Duration(milliseconds: 300),
);
}
void _setlatlngBounds(MaplibreMapController? mController){
//animate camera to the Latlng bounds with 2 Latlng points, the southwest and northeast corner position of the map viewport
mController?.animateCamera(
CameraUpdate.newLatLngBounds(
LatLngBounds(
southwest: const LatLng(23.878921, 90.345552),
northeast: const LatLng(23.736799, 90.451606)
),
left: 10, // padding in 4 directions
top: 5,
bottom: 25,
right: 10
),
);
}
void _scrollby(MaplibreMapController? mController){
//scroll the map programatically in x and y axis
mController?.animateCamera(
CameraUpdate.scrollBy(150.0, -225.0),
);
}
void _zoomBy( MaplibreMapController? mController){
//zoom in/out the map in current position, negative values for zoom out , positive for zoom in
mController?.animateCamera(
CameraUpdate.zoomBy(-0.5),
);
}
```
---
## Getting Started(Barikoi Map Flutter)
Barikoi Documentation
To implement Barikoi map in you android app, we recommend using Flutter Maplibre GL as it is quick and easy to install and use with Barikoi maps.
### Prerequisite
For this example you will need:
1. A Barikoi API key, which you can get in [Developer Dashboard](https://developer.barikoi.com "Developer Dashboard")
2. Knowledge in Flutter, Dart
3. Barikoi map style
### Get Dependencies
Add maplibre_gl to your project by running this command:
```js
flutter pub add maplibre_gl
```
or add it directly as a dependency to your pubspec.yaml file:
```yaml
dependencies:
maplibre_gl: ^0.19.0
```
#### Web
Include the following JavaScript and CSS files in the head of the web/index.html file.
```js
```
#### Location Features
#### Android
Add the ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission in the application manifest android/app/src/main/AndroidManifest.xml to enable location features in an Android application:
```xml
```
Starting from Android API level 23 you also need to request it at runtime. This plugin does not handle this for you. The example app uses the flutter 'location' plugin for this.
#### iOS
To enable location features in an iOS application:
If you access your users' location, you should also add the following key to ios/Runner/Info.plist to explain why you need access to their location data:
```xml
NSLocationWhenInUseUsageDescription[Your explanation here]
```
A possible explanation could be: "Shows your location on the map".
You can check out the various functionalities in the sample code snippets listed in ths documentation. To have a more detailed understanding of the classes and functionalities, check out this [Documentation](https://htmlpreview.github.io/?https://raw.githubusercontent.com/maplibre/flutter-maplibre-gl/docs/doc/api/maplibre_gl/maplibre_gl-library.html)
---
## Barikoi map place picker
Barikoi Documentation
Barikoi Map Place picker is a flutter library that provides option to pick a place from map location or search using Barikoi Map and APIs.
# Installation
Github
If this Dart package is published to Github, please include the following in pubspec.yaml
```js
//github.com/barikoi/barikoi_map_place_picker.git
dependencies:
barikoi_maps_place_picker:
git:
url: https://github.com/barikoi/barikoi_map_place_picker.git
ref: main
#or specific commit
```
instead of main you can specify the specific commit that works for you
Local
To use the package in your local drive, please include the following in pubspec.yaml
```js
dependencies:
barikoi_maps_place_picker:
path: /path/to/barikoi_map_place_picker
```
# Getting Started
For iOS platform, the following codes are no longer needed to be added in your pod file, if you have already used the previous version of this library please remove the following lines from your pod file:
View obsolete code
```ruby
source 'https://cdn.cocoapods.org/'
source 'https://github.com/m0nac0/flutter-maplibre-podspecs.git'
pod 'MapLibre'
pod 'MapLibreAnnotationExtension'
```
open your info.plist file and add these string resources
```xml ...
NSLocationWhenInUseUsageDescription[Your explanation here]
```
For android, add the location permissions in the app manifest
```js
```
To use the place picker as a widget, add the following code
```js
PlacePicker(
apiKey: "API_KEY", //Barikoi API key
initialPosition: HomePage.kInitialPosition, //initial location position to start the map with
useCurrentLocation: true, // option to use the current location for picking a place, true by default
selectInitialPosition: true, //option to load the initial position to start the map with
usePinPointingSearch: true, //option to use reversegeo api to get place from location point, default value is true
getAdditionalPlaceData: [ PlaceDetails.area_components, PlaceDetails.addr_components, PlaceDetails.district ] //option to retrive addtional place data, will count extra api calls
onPlacePicked: (result) { //returns the place object selected in the place picker
selectedPlace = result;
},
);
```
---
## Installation and Setting Up(Python)
# Barikoi APIs - Python SDK
[](https://pypi.org/project/barikoiapis/)
[](https://www.python.org/)
[](https://opensource.org/licenses/MIT)
## Description
**Barikoi APIs** is the official Python SDK for [Barikoi Location Services](https://barikoi.com). Built with modern Python best practices, it provides a type-safe interface for accessing a wide range of location-based services including search, geocoding, reverse geocoding, routing, and more.
This SDK is optimized for modern Python applications including Django, Flask, FastAPI, and standalone Python scripts.
## Features
- **Type-Safe** - Full type hints with Pydantic models for data validation
- **Built-in Validation** - Automatic request/response validation with clear error messages
- **Runtime API Key Management** - Update API keys dynamically without reinitializing the client
- **Modern Async Support** - Both synchronous and asynchronous API calls
- **Custom Error Classes** - ValidationError, BarikoiError, TimeoutError
- **Easy to Use** - Intuitive Pythonic interface
- **Well Documented** - Comprehensive documentation with examples
## Getting Started
### Get Barikoi API Key
To access Barikoi's API services, you need to:
1. Register on [Barikoi Developer Dashboard](https://developer.barikoi.com/register)
2. Verify with your phone number
3. Claim your API key
Once registered, you'll be able to access the full suite of Barikoi API services. If you exceed the free usage limits, you'll need to subscribe to a paid plan.
## Installation
Install the package using pip:
```bash
pip install barikoiapis
```
Or using poetry:
```bash
poetry add barikoiapis
```
## Quick Start
```python
from barikoiapis import BarikoiClient
# Initialize the client
client = BarikoiClient(api_key="YOUR_BARIKOI_API_KEY")
# Search for places with autocomplete
result = client.autocomplete(q="Dhaka", bangla=True)
places = result.places or []
```
## API Reference
### Quick Navigation
1. [autocomplete](#autocomplete) - Search for places with autocomplete suggestions
2. [reverseGeocode](#reversegeocode) - Convert coordinates to addresses
3. [nearbyPlaces](#nearbyplaces) - Find places within a radius
4. [geocode](#geocode) - Format and geocode addresses
5. [searchPlace](#searchplace) - Search for places with session management
6. [placeDetails](#placedetails) - Get detailed place information
7. [routeOverview](#routeoverview) - Get route information between points
8. [calculateRoute](#calculateroute) - Detailed route with turn-by-turn instructions
9. [snapToRoad](#snaptoroad) - Find nearest point on road network
10. [checkNearby](#checknearby) - Verify proximity within a radius
---
### autocomplete
Search for places with autocomplete suggestions. Returns matching places with
addresses in `English` and `Bangla`, coordinates, and place details. Use for search
boxes, address forms, and location pickers.
```python
result = client.autocomplete(
q="Dhaka",
bangla=True
)
places = result.places or []
```
**Parameters:**
- `q` (str, required): Search query string (min length: 1)
- `bangla` (bool, optional): Include Bangla language fields (default: True)
**Response Fields:**
- `places` (List[Dict], optional): List of matching places
- `id` (int, optional): Place ID
- `longitude` (str | float, optional): Longitude coordinate
- `latitude` (str | float, optional): Latitude coordinate
- `address` (str, optional): Address in English
- `address_bn` (str, optional): Address in Bangla
- `city` (str, optional): City name
- `city_bn` (str, optional): City name in Bangla
- `area` (str, optional): Area name
- `area_bn` (str, optional): Area name in Bangla
- `postCode` (str | int, optional): Postal code
- `pType` (str, optional): Place type
- `uCode` (str, optional): Unique code
- `status` (int, optional): Response status code
---
### reverseGeocode
Convert coordinates to human-readable addresses with administrative details (district, division, thana) in `English` and `Bangla`. Use for displaying user location, delivery
addresses, and location tagging.
**IMPORTANT:** ⚠️ Enabling all optional parameters triggers multiple API calls, consuming more credits. Request only essential parameters.
```python
result = client.reverseGeocode(
latitude=23.8103,
longitude=90.4125,
district=True,
bangla=True
)
place = result.place
```
**Parameters:**
- `longitude` (float, required): Longitude coordinate (range: -180 to 180)
- `latitude` (float, required): Latitude coordinate (range: -90 to 90)
- `country_code` (str, optional): Country code (ISO Alpha-2 format, e.g., 'BD') (default: 'BD')
- `country` (bool, optional): Include country information
- `district` (bool, optional): Include district information
- `post_code` (bool, optional): Include postal code
- `sub_district` (bool, optional): Include sub-district information
- `union` (bool, optional): Include union information
- `pauroshova` (bool, optional): Include pauroshova information
- `location_type` (bool, optional): Include location type
- `division` (bool, optional): Include division information
- `address` (bool, optional): Include address
- `area` (bool, optional): Include area information
- `bangla` (bool, optional): Include Bangla translations
- `thana` (bool, optional): Include thana information
**Response Fields:**
- `place` (Dict, optional): Place information
- `id` (str | int, optional): Place ID
- `distance_within_meters` (float, optional): Distance in meters
- `address` (str, optional): Address in English
- `area` (str, optional): Area name
- `city` (str, optional): City name
- `postCode` (str, optional): Postal code
- `address_bn` (str, optional): Address in Bangla
- `area_bn` (str, optional): Area in Bangla
- `city_bn` (str, optional): City in Bangla
- `country` (str, optional): Country name
- `division` (str, optional): Division name
- `district` (str, optional): District name
- `sub_district` (str, optional): Sub-district name
- `pauroshova` (str | None, optional): Pauroshova name
- `union` (str | None, optional): Union name
- `location_type` (str, optional): Location type
- `address_components` (Dict | None, optional): Address components
- `area_components` (Dict | None, optional): Area components
- `thana` (str, optional): Thana name
- `thana_bn` (str, optional): Thana name in Bangla
- `status` (int, optional): Response status code
---
### nearbyPlaces
Find places within a specified radius. Returns nearby locations sorted by distance with names, addresses, and coordinates. Perfect for "nearby stores", POI discovery, restaurant finders, and ATM locators.
```python
result = client.nearbyPlaces(
latitude=23.8103,
longitude=90.4125,
radius=1.0,
limit=20
)
places = result.places or []
```
**Parameters:**
- `longitude` (float, required): Longitude coordinate (range: -180 to 180)
- `latitude` (float, required): Latitude coordinate (range: -90 to 90)
- `radius` (float, optional): Search radius in kilometers (range: 0.1 to 100, default: 0.5)
- `limit` (int, optional): Maximum number of results (range: 1 to 100, default: 10)
**Response Fields:**
- `places` (List[Dict], optional): List of nearby places
- `id` (int, optional): Place ID
- `name` (str, optional): Place name
- `distance_in_meters` (str | float, optional): Distance from query point
- `longitude` (str, optional): Longitude
- `latitude` (str, optional): Latitude
- `pType` (str, optional): Place type
- `Address` (str, optional): Full address
- `area` (str, optional): Area name
- `city` (str, optional): City name
- `postCode` (str, optional): Postal code
- `subType` (str, optional): Place sub-type
- `uCode` (str, optional): Unique code
- `status` (int, optional): Response status code
---
### geocode
Validate and format addresses with completeness status and confidence score. Returns standardized address format. Use for checkout validation, delivery verification, and CRM data cleaning.
**Note:** Uses 2 API calls internally.
```python
result = client.geocode(
q="house 23, road 5, mirpur dhaka",
district="yes",
bangla="yes"
)
status = result.address_status
confidence = result.confidence_score_percentage
fixed = result.fixed_address
```
**Parameters:**
- `q` (str, required): Address to geocode (min length: 1)
- `thana` (str, optional): Include thana ('yes' or 'no')
- `district` (str, optional): Include district ('yes' or 'no')
- `bangla` (str, optional): Include Bangla ('yes' or 'no')
**Response Fields:**
- `given_address` (str, optional): Original input address
- `fixed_address` (str, optional): Corrected/standardized address
- `bangla_address` (str, optional): Address in Bangla
- `address_status` (str, optional): 'complete' or 'incomplete'
- `geocoded_address` (Dict, optional): Detailed geocoded information
- `confidence_score_percentage` (float, optional): Confidence score (0-100)
- `status` (int, optional): Response status code
---
### searchPlace
Search for places and get unique place codes with a session ID. Returns matching places with addresses. Use for business search, landmark lookup, and location selection.
**Note:** Each request generates a new session ID required for Place Details API.
```python
search_result = client.searchPlace(q="barikoi")
session_id = search_result.session_id
places = search_result.places or []
```
**Parameters:**
- `q` (str, required): Search query string (min length: 1)
**Response Fields:**
- `places` (List[Dict], optional): List of matching places
- `address` (str, optional): Place address
- `place_code` (str, optional): Unique place code
- `session_id` (str, optional): Session ID for place details
- `status` (int, optional): Response status code
---
### placeDetails
Get detailed place information using a place code and session ID. Returns complete address and coordinates. Use after Search Place to fetch full location data.
**Note:** Requires place code and session ID from Search Place request.
```python
details = client.placeDetails(
place_code="BKOI2017",
session_id=session_id
)
place = details.place
```
**Parameters:**
- `place_code` (str, required): Place code from search place results (min length: 1)
- `session_id` (str, required): Session ID from search place results (min length: 1)
**Response Fields:**
- `place` (Dict, optional): Place information
- `address` (str, optional): Full address
- `place_code` (str, optional): Place code
- `latitude` (str, optional): Latitude
- `longitude` (str, optional): Longitude
- `session_id` (str, optional): Session ID
- `status` (int, optional): Response status code
---
### routeOverview
Get route information between geographical points. Returns route geometry, distance, duration, and waypoints in polyline or GeoJSON format. Use for displaying routes, calculating distances, and showing ETAs.
**Note:** Coordinates must be in "longitude,latitude" format.
```python
result = client.routeOverview(
coordinates="90.4125,23.8103;90.4000,23.8000",
geometries="geojson"
)
route = result.routes[0] if result.routes else None
distance_km = route.distance / 1000 if route else 0
duration_min = route.duration / 60 if route else 0
```
**Parameters:**
- `coordinates` (str, required): Coordinates in format "lon,lat;lon,lat" (e.g., "90.4125,23.8103;90.3742,23.7461")
- `geometries` (str, optional): Geometry format ('polyline', 'polyline6', or 'geojson') (default: 'polyline')
- `profile` (str, optional): Transportation profile ('car' or 'foot') (default: 'car')
**Response Fields:**
- `code` (str, optional): Response code
- `routes` (List[Dict], optional): List of routes
- `geometry` (str | Dict, optional): Route geometry (polyline string or GeoJSON object)
- `legs` (List[Dict], optional): Route legs
- `distance` (float, optional): Distance in meters
- `duration` (float, optional): Duration in seconds
- `weight_name` (str, optional): Weight name
- `weight` (float, optional): Weight value
- `waypoints` (List[Dict], optional): Waypoint information
---
### calculateRoute
Get detailed route information powered by GraphHopper routing engine. Returns comprehensive route data including path coordinates, distance, travel time, turn-by-turn instructions with street names, and elevation data (ascend/descend). Use for GPS navigation apps, route planning, delivery optimization, and mapping applications requiring detailed routing information.
```python
result = client.calculateRoute(
start={"latitude": 23.8103, "longitude": 90.4125},
destination={"latitude": 23.8, "longitude": 90.4},
type="gh",
profile="car"
)
hints = result.hints
paths = result.paths
```
**Parameters:**
- `start` (Dict, required): Start point coordinates
- `latitude` (float, required): Start latitude (range: -90 to 90)
- `longitude` (float, required): Start longitude (range: -180 to 180)
- `destination` (Dict, required): Destination point coordinates
- `latitude` (float, required): Destination latitude (range: -90 to 90)
- `longitude` (float, required): Destination longitude (range: -180 to 180)
- `type` (str, required): Route calculation type ('gh')
- `profile` (str, optional): Transportation profile ('car', 'motorcycle', or 'bike')
**Response Fields:**
- `hints` (Dict, optional): Route calculation hints
- `info` (Dict, optional): Additional information
- `paths` (List[Dict], optional): Route paths with detailed information
- `distance` (float, optional): Distance in meters
- `time` (int, optional): Time in milliseconds
- `points` (Dict, optional): GeoJSON LineString with coordinates
- `instructions` (List[Dict], optional): Turn-by-turn instructions
- `ascend` (float, optional): Total ascent in meters
- `descend` (float, optional): Total descent in meters
---
### snapToRoad
Find the nearest road point to given coordinates. Returns snapped coordinates and distance to road. Use for vehicle tracking, GPS trace alignment, and ride-sharing location accuracy.
```python
result = client.snapToRoad(
point="23.8103,90.4125" # Format: latitude,longitude
)
snapped = result.coordinates
distance = result.distance
```
**Parameters:**
- `point` (str, required): Point coordinates in format "latitude,longitude" (e.g., "23.8103,90.4125")
**Response Fields:**
- `coordinates` (Tuple[float, float], optional): Snapped coordinates [longitude, latitude]
- `distance` (float, optional): Distance to road in meters
- `type` (str, optional): Geometry type ('Point')
---
### checkNearby
Verify if a location is within a specified radius. Returns "Inside geo fence" or "Outside geo fence" status. Perfect for delivery notifications, driver alerts, proximity triggers, and employee check-in from devices in HR applications.
```python
result = client.checkNearby(
current_latitude=23.8103,
current_longitude=90.4125,
destination_latitude=23.8,
destination_longitude=90.4,
radius=100
)
is_inside = result.message == "Inside geo fence"
```
**Parameters:**
- `destination_latitude` (float, required): Destination latitude (range: -90 to 90)
- `destination_longitude` (float, required): Destination longitude (range: -180 to 180)
- `radius` (float, required): Radius in meters (range: 1 to 1000)
- `current_latitude` (float, required): Current latitude (range: -90 to 90)
- `current_longitude` (float, required): Current longitude (range: -180 to 180)
**Response Fields:**
- `message` (str, optional): Status message ("Inside geo fence" or "Outside geo fence")
- `status` (int, optional): Response status code
- `data` (Dict, optional): Additional geofence data
---
### API Key Management
```python
# Set during initialization
client = BarikoiClient(api_key="YOUR_BARIKOI_API_KEY")
# Update API key at runtime
client.setApiKey("new-api-key")
# Get current API key
current_key = client.getApiKey()
```
### Timeout Configuration
```python
# Set timeout during initialization (in seconds, default: 30)
client = BarikoiClient(
api_key="YOUR_KEY",
timeout=60 # 60 seconds
)
```
### Custom Base URL
```python
client = BarikoiClient(
api_key="YOUR_KEY",
base_url="https://custom-endpoint.barikoi.xyz"
)
```
### Async Support
```python
from barikoiapis import AsyncBarikoiClient
async def main():
client = AsyncBarikoiClient(api_key="YOUR_KEY")
result = await client.autocomplete(q="Dhaka", bangla=True)
places = result.places or []
await client.close()
asyncio.run(main())
```
---
## Documentation
- [API Documentation](https://docs.barikoi.com/api) - Official Barikoi API docs
- [Python Documentation](https://docs.python.org/3/) - Python documentation
- [Pydantic Documentation](https://docs.pydantic.dev/) - Validation library docs
---
## Support Resources
- [Barikoi Website](https://www.barikoi.com/)
- [Developer Portal](https://developer.barikoi.com/)
- [Documentation](https://docs.barikoi.com/docs/maps-api)
- [Support Email](mailto:hello@barikoi.com)
---
## License
This library is licensed under the MIT License. See the [LICENSE](https://pypi.org/project/LICENSE/) file for details.
---
## Barikoi Laravel Package
A comprehensive Laravel package for integrating [Barikoi API](https://barikoi.com) - Bangladesh's leading location data provider.
[](LICENSE.md)
## Features
- 🗺️ **Location Services**: Geocoding, Reverse Geocoding, Autocomplete, Place Search, Place Details, Nearby Places, Check nearby location within a specified radius, Snap to Road
- 🛣️ **Routing**: Calculate Detailed Route and Route Overview
- ⚠️ **Error Handling**: User-friendly exceptions with actionable error messages
---
## Installation
```bash
composer require barikoi/barikoiapis
```
## Configuration
1. Publish the configuration file:
```bash
php artisan vendor:publish --provider="Barikoi\BarikoiApis\BarikoiServiceProvider" --tag="config"
```
2. Add your Barikoi API credentials to `.env`:
```env
BARIKOI_API_KEY=your_api_key_here
```
Get your API key from [Barikoi](https://developer.barikoi.com).
---
## Quick Start
```php
use Barikoi\BarikoiApis\Facades\Barikoi;
// 1. Reverse geocoding with rich options
$options = [
'country_code' => 'BD',
'district' => true,
'post_code' => true,
'country' => true,
'sub_district' => true,
'union' => true,
'pauroshova' => true,
'location_type' => true,
'division' => true,
'address' => true,
'area' => true,
'bangla' => true,
'thana' => true,
];
$reverse = Barikoi::reverseGeocode(90.3572, 23.8067, $options);
$autocomplete=Barikoi::autocomplete('barikoi', [
'bangla' => true,
'city' => 'dhaka',
'sub_area' => true,
'sub_district' => true,
]);
// Geocode (Rupantor) - returns stdClass (object)
$geocoded = Barikoi::geocode('shawrapara');
// Search place by text
$places = Barikoi::searchPlace('Dhanmondi');
// Get place details by place_code - returns stdClass (object)
$placeDetails = Barikoi::placeDetails('BKOI2017', [
'session_id' => '4c47157f-22d6-4689-abdf-c9f81eb43ae4'
]);
// Nearby search
$nearby = Barikoi::nearby(90.38305163, 23.87188719, 0.5, 2);
// Check nearby (geofence check)
$checkNearby = Barikoi::checkNearby(23.8067, 90.3572, 23.8070, 90.3575, 50);
// Snap to nearest road
$snapped = Barikoi::snapToRoad(23.8067, 90.3572);
// Detailed route between two points (returns stdClass object with "trip")
$route = Barikoi::calculateRoute([
'start' => ['longitude' => 90.36558776260725, 'latitude' => 23.791645065364126],
'destination' => ['longitude' => 90.3676300089066, 'latitude' => 23.784715477921843],
], [
'type' => 'vh', // 'vh' (motorcycle only) or 'gh' (all profiles)
'profile' => 'motorcycle' // 'bike', 'motorcycle', or 'car'
]);
// Simple route overview (returns stdClass object)
$overview = Barikoi::routeOverview([
['longitude' => 90.3572, 'latitude' => 23.8067],
['longitude' => 90.3680, 'latitude' => 23.8100],
], [
'profile' => 'car',
'geometries' => 'polyline',
]);
```
---
## Documentation
### 📚 API Documentation
Complete documentation with parameters, conditions, and error handling for each API:
| API Service | Documentation |
|-------------|---------------|
| **Location Services** | [docs/location-api.md](docs/location-api.md) |
| - Reverse Geocoding | Convert coordinates to address |
| - Geocoding (Rupantor) | Convert address to coordinates |
| - Autocomplete | Place suggestions |
| - Search Place | Text-based place search |
| - Place Details | Get Place Details |
| - Nearby Search | Find places within radius |
| - Check Nearby | Check nearby location within a specified radius |
| - Snap to Road | Correct GPS coordinates |
| | |
| **Routing Services** | [docs/routing-api.md](docs/routing-api.md) |
| - Route Overview | Simple route calculation |
| - Detailed Route | Turn-by-turn route with options |
---
## Usage
### Using Facade (Recommended)
```php
use Barikoi\BarikoiApis\Facades\Barikoi;
$reverse = Barikoi::reverseGeocode(90.3572, 23.8067); // returns stdClass (object)
$places = Barikoi::autocomplete('restaurant');
```
---
## Error Handling
The package provides comprehensive error handling with user-friendly messages.
### Exception Types
**`BarikoiValidationException`** - Validation errors (400)
- Invalid coordinates
- Missing parameters
- Invalid input values
**`BarikoiApiException`** - API errors (401, 404, 429, 500, etc.)
- 401: Invalid API key
- 404: Resource not found
- 429: Rate limit exceeded
- 500: Server error
### Basic Usage
```php
use Barikoi\BarikoiApis\Facades\Barikoi;
use Barikoi\BarikoiApis\Exceptions\BarikoiApiException;
use Barikoi\BarikoiApis\Exceptions\BarikoiValidationException;
try {
$result = Barikoi::reverseGeocode(90.3572, 23.8067);
} catch (BarikoiValidationException $e) {
// Handle validation errors (400)
echo "Invalid input: " . $e->getMessage();
} catch (BarikoiApiException $e) {
// Handle API errors (401, 404, 500, etc.)
echo "API Error: " . $e->getMessage();
}
```
### Error Messages
| Code | Message |
|------|---------|
| 400 | `Validation Error: Invalid coordinates. Please check your input parameters.` |
| 401 | `Authentication Failed: Invalid API key. Please verify your API key is correct.` |
| 404 | `Not Found: Resource not found. The requested resource or endpoint does not exist.` |
| 429 | `Rate Limit Exceeded: Too many requests. Please reduce the number of requests...` |
| 500 | `Server Error: Internal Server Error. The Barikoi API is experiencing issues...` |
### Getting Error Details
```php
catch (BarikoiValidationException $e) {
$message = $e->getMessage(); // User-friendly message
$statusCode = $e->getCode(); // HTTP status code
}
```
See individual API documentation for specific error conditions and solutions.
---
## Examples
### Example 1: Get Address from GPS
```php
public function getAddress(Request $request)
{
try {
$result = Barikoi::reverseGeocode(
$request->longitude,
$request->latitude,
['district' => true, 'bangla' => true]
);
return response()->json([
'address' => $result->place->address,
'district' => $result->place->district,
]);
} catch (BarikoiApiException $e) {
return response()->json(['error' => $e->getMessage()], $e->getCode());
}
}
```
### Example 2: Calculate Route
```php
public function calculateRoute(Request $request)
{
try {
$route = Barikoi::calculateRoute([
'start' => ['longitude' => $request->start_longitude, 'latitude' => $request->start_latitude],
'destination' => ['longitude' => $request->destination_longitude, 'latitude' => $request->destination_latitude],
], [
'type' => 'vh',
'profile' => 'motorcycle'
]);
return $route;
} catch (BarikoiApiException $e) {
return response()->json(['error' => 'Route calculation failed'], 500);
}
}
```
---
## API Reference
For detailed Barikoi API documentation, visit [Barikoi API Documentation](https://docs.barikoi.com/api).
---
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for version history.
---
## License
The MIT License (MIT). See [License File](LICENSE.md) for more information.
---
## Credits
- [Barikoi Technologies Limited](https://www.barikoi.com/)
- Package developed for easy Laravel integration
---
## Support
For issues or questions:
- Create an issue on GitHub
- Check the detailed API documentation in `docs/` folder
- Contact support@barikoi.com for any support
---
## Domain Binding
Domain Binding | Barikoi Documentation
## Overview
Domain binding allows you to restrict your API key usage to specific domains. This security feature prevents unauthorized websites from using your API key, protecting your quota and ensuring only your approved domains can make API requests.
## How Domain Binding Works
When domain binding is enabled:
- Only requests from your approved domains will be accepted
- Requests from unauthorized domains will be blocked
- This applies to browser-based requests (CORS protection)
## Adding Domains
### Step 1: Access API Management
1. Log in to the [Barikoi Developer Portal](https://developer.barikoi.com)
2. Navigate to **API Management** from the dashboard
### Step 2: Open Domain Settings
1. Locate your API key in the list
2. Click the **Manage domains** icon (link icon) in the Operations column
3. A modal will appear for managing domains
### Step 3: Add Your Domain(s)
1. In the **Add Domain(s)** field, enter your domain name
2. Click **Add** to confirm
3. Repeat for additional domains if needed
## Domain Format Guidelines
| Format | Example | Notes |
|--------|---------|-------|
| Full domain | `example.com` | Matches exact domain |
| With subdomain | `www.example.com` | Matches specific subdomain |
| Wildcard | `*.example.com` | Matches all subdomains |
## Best Practices
- **Only add domains you control** - Do not share your API key with untrusted parties
- **Use wildcards carefully** - `*.example.com` allows any subdomain to use your key
- **Include both www and non-www** - Add both `example.com` and `www.example.com` if needed
- **Test after adding** - Verify that your application still works after enabling domain binding
## Troubleshooting
### API Requests Blocked
If your requests are being blocked after enabling domain binding:
1. **Check the domain format** - Ensure the domain matches exactly (including protocol)
2. **Verify subdomain** - If using a subdomain, ensure it's included in the allowed list
3. **Clear browser cache** - Cached responses may cause issues
### Local Development
For local development, you may need to add:
- `localhost`
- `127.0.0.1`
- Your local development domain (e.g., `dev.example.com`)
## Related Topics
- [API Usage & Pricing](/pricing/pricing-call)
- [API Reference](/api)
---
## Animate Map Camera
Bring your map to life with **smooth, cinematic camera animations** — rotate, fly, zoom, and pitch dynamically using `flyTo`, `easeTo`, or `requestAnimationFrame`.
Perfect for:
- App intros & onboarding
- Property tours
- City flyovers
- Storytelling & presentations
- 3D building showcases
---
## Full Working Example
```html
Cinematic Camera Animation - Barikoi GL
```
---
## Camera Animation Methods
| Method | Best For | Smoothness |
| ------------------------------------------ | ---------------------- | ------------ |
| `map.flyTo()` | Cinematic jumps, tours | Highest |
| `map.easeTo()` | Gentle transitions | Very High |
| `map.jumpTo()` | Instant (no animation) | None |
| `setBearing()` + `requestAnimationFrame()` | Continuous rotation | Ultra-smooth |
**Pro Tip**: Always use `duration: 0` with `requestAnimationFrame` for 60fps rotation.
---
## Advanced: Orbital + Tilt Animation
```js
let time = 0;
function animate() {
time += 0.01;
map.setBearing(time * 20);
map.setPitch(45 + Math.sin(time) * 20);
requestAnimationFrame(animate);
}
```
Creates a **breathing orbital effect** — mesmerizing!
---
Make your map **breathtaking** — literally.
---
## Animate Point Along Line
Create a **beautiful, smooth animation** of a moving icon (car, plane, ship, person) traveling along any route — with automatic rotation to follow the path.
---
## Features
- Smooth 60fps animation using `requestAnimationFrame`
- Automatic bearing rotation (icon faces direction of travel)
- Replay button
- Works with any route (curved or straight)
- Powered by **Turf.js** for precise geodesic calculations
---
## Full Working Example
```html
Animate Point Along Line - Barikoi GL
```
---
## Popular Use Cases
| Icon | Use Case |
| ------ | ---------------------------- |
| Car | Live delivery tracking |
| Plane | Flight path animation |
| Ship | Maritime route visualization |
| Person | Walking tour / pilgrimage |
| Bus | Public transit simulation |
---
## Customization Tips
| Want to change... | How to do it |
| ----------------- | -------------------------------------------- |
| Speed | Reduce `steps` (e.g., 300 = faster) |
| Smoothness | Increase `steps` (e.g., 1000 = ultra smooth) |
| Icon | Replace image URL with your own |
| Route | Use real GPS track or routing API |
| Auto-loop | Reset `counter = 0` when animation ends |
---
## Advanced: Real-time Vehicle Tracking
```js
// Simulate live GPS updates
setInterval(() => {
const newCoord = getLiveGPS(); // your API
arc.push(newCoord);
route.geometry.coordinates = arc;
map.getSource("route").setData(route);
}, 5000);
```
---
Bring your routes to **life** — beautifully.
---
## Click Marker to Center Map
Enable users to click any marker and have the map elegantly fly to that location. Perfect for directories, store locators, or exploring multiple points of interest.
---
## Quick Usage
```js
map.on("click", "your-layer-id", (e) => {
map.flyTo({
center: e.features[0].geometry.coordinates,
zoom: 15,
duration: 2000,
});
});
```
---
## Full Working Example
```html
Click Marker to Center Map - Barikoi GL
```
---
## Code Breakdown
### 1. Load Custom Icon
```js
const image = await map.loadImage("url-to-icon.png");
map.addImage("custom-marker", image.data);
```
Use any PNG with transparency for beautiful custom markers.
### 2. Add Points via GeoJSON
```js
map.addSource("points", { type: "geojson", data: { ... } });
```
Best practice: Use GeoJSON for dynamic, scalable point data.
### 3. Create Clickable Symbol Layer
```js
map.addLayer({
id: "poi-markers",
type: "symbol",
source: "points",
layout: { "icon-image": "custom-marker" },
});
```
Only `symbol` layers with `icon-image` are clickable by default.
### 4. Fly to Location on Click
```js
map.on("click", "poi-markers", (e) => {
map.flyTo({
center: e.features[0].geometry.coordinates,
zoom: 15,
duration: 2000,
});
});
```
`flyTo` gives a smooth cinematic animation. Use `easeTo` or `jumpTo` for alternatives.
---
## flyTo Options
| Option | Description | Recommended Value |
| ---------- | ------------------------ | -------------------- |
| `zoom` | Target zoom level | `14–17` |
| `duration` | Animation time (ms) | `1500–2500` |
| `speed` | Speed of camera movement | `1.2` (default) |
| `curve` | How sharp the turn is | `1.42` (smooth) |
| `easing` | Timing function | Cubic Bézier (above) |
---
## Advanced: Highlight Clicked Marker
```js
let activeMarker = null;
map.on("click", "poi-markers", (e) => {
const coords = e.features[0].geometry.coordinates;
// Change icon size on click
map.setLayoutProperty("poi-markers", "icon-size", 0.04);
if (activeMarker) {
map.setLayoutProperty("poi-markers", "icon-size", 0.04);
}
// Enlarge clicked one
map.setLayoutProperty("poi-markers", "icon-size", [
"match",
["get", "name"],
e.features[0].properties.name,
0.12,
0.04,
]);
map.flyTo({ center: coords, zoom: 16 });
});
```
---
## Bonus: Fit Bounds for Multiple Points
```js
map.fitBounds(
[
[90.34, 23.7],
[90.44, 23.84],
],
{ padding: 80, duration: 2000 }
);
```
Great for "Show all locations" button.
---
Happy mapping!
---
## Fit Bounds
Use `map.fitBounds()` to instantly frame any geographic data — whether it's a route, a group of stores, or a delivery zone — with perfect padding and smooth animation.
---
## Quick Usage
```js
const bounds = new bkoigl.LngLatBounds(
[90.35, 23.7], // southwest
[90.44, 23.84] // northeast
);
map.fitBounds(bounds, {
padding: 80,
duration: 2000,
});
```
---
## Full Working Example
```html
Fit Bounds - Barikoi GL
```
---
## How to Calculate Bounds
### Method 1: From Array of Coordinates (Recommended)
```js
const bounds = coordinates.reduce(
(b, coord) => b.extend(coord),
new bkoigl.LngLatBounds(coordinates[0], coordinates[0])
);
```
### Method 2: Manual Southwest & Northeast
```js
const bounds = new bkoigl.LngLatBounds(
[90.35, 23.7], // SW corner
[90.44, 23.84] // NE corner
);
```
### Method 3: From GeoJSON Feature
```js
const bounds = new bkoigl.LngLatBounds();
geojson.features.forEach((feature) => {
bkoigl.LngLatBounds.convert(feature.bbox || feature.geometry).extend(bounds);
});
```
---
## fitBounds Options
| Option | Type | Description | Recommended |
| ---------- | ------------- | ----------------------------------- | ---------------------------------- |
| `padding` | Number/Object | Pixels from edge | `80` or `{top:100, left:120, ...}` |
| `duration` | Number | Animation time (ms) | `1500–2500` |
| `maxZoom` | Number | Prevent zooming in too far | `16–17` |
| `bearing` | Number | Rotate map during fit | `0–360` |
| `pitch` | Number | 3D tilt | `0–60` |
| `linear` | Boolean | Disable easing (straight animation) | `false` (default) |
---
## Advanced: Fit Bounds with Custom Padding Object
```js
map.fitBounds(bounds, {
padding: {
top: 120,
bottom: 180, // Extra space for bottom UI
left: 100,
right: 100,
},
duration: 2000,
});
```
Perfect when you have a bottom sheet, sidebar, or floating controls.
---
## Pro Tip: Auto-fit on Data Load
```js
map.on("load", () => {
// Automatically fit bounds after adding data
map.fitBounds(bounds, { padding: 100, duration: 1800 });
});
```
---
Perfect framing, every time!
---
## Fly to Location
Use `map.flyTo()` to create smooth, cinematic transitions to any coordinate. Ideal for navigation buttons, "Locate HQ", or guiding users to important places.
---
## Quick Usage
```js
map.flyTo({
center: [90.4071, 23.7925], // Dhaka coordinates
zoom: 15,
speed: 1.2,
curve: 1.42,
easing: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2),
});
```
---
## Full Working Example
```html
Fly to Location - Barikoi GL
```
---
## Code Breakdown
### 1. Basic `flyTo`
```js
map.flyTo({
center: [lng, lat],
zoom: 15,
duration: 2000, // milliseconds
});
```
### 2. Advanced Animation Options
| Option | Type | Description | Best For |
| ---------- | -------- | ------------------------ | ------------------------- |
| `zoom` | Number | Target zoom level | `14–18` |
| `center` | Array | `[lng, lat]` coordinates | Required |
| `duration` | Number | Animation time (ms) | `1500–3000` |
| `speed` | Number | Speed multiplier | `0.6` (slow) – `2` (fast) |
| `curve` | Number | How sharp the turn is | `1.42` (smooth) |
| `bearing` | Number | Map rotation in degrees | `0–360` |
| `pitch` | Number | Tilt angle (3D effect) | `0–60` |
| `easing` | Function | Custom timing function | Smooth cinematic feel |
---
## Pro Tip: Smooth Easing Function
```js
easing: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2);
```
---
## Advanced: Fly with Marker + Popup
```js
map.flyTo({ center: coords, zoom: 16, duration: 2500 });
setTimeout(() => {
new bkoigl.Marker()
.setLngLat(coords)
.setPopup(new bkoigl.Popup({ offset: 25 }).setHTML("Welcome!"))
.addTo(map)
.togglePopup();
}, 2600);
```
---
## Alternative Methods
| Method | Use Case |
| ----------------- | ---------------------------------------- |
| `map.flyTo()` | Smooth cinematic animation (recommended) |
| `map.easeTo()` | Slightly faster, still smooth |
| `map.jumpTo()` | Instant jump (no animation) |
| `map.fitBounds()` | Show multiple locations at once |
---
---
## Live Real-Time Data
Display **live-updating markers** that move smoothly across the map — powered by real-time data sources (WebSocket, API polling, MQTT, etc.).
Perfect for:
- Live vehicle/fleet tracking
- Flight radar
- Delivery status
- IoT sensor locations
- Ride-hailing apps
---
## Full Working Example
```html
Live Real-Time Tracking - Barikoi GL
Live Tracking Active
Vehicle ID: DHK-247
Waiting for update...
```
---
## Real-World Integration (Replace Simulation)
```js
// Example: Fetch from your backend API
setInterval(async () => {
const res = await fetch("/api/vehicles/live");
const vehicles = await res.json();
map.getSource("live-vehicles").setData({
type: "FeatureCollection",
features: vehicles.map((v) => ({
type: "Feature",
geometry: { type: "Point", coordinates: [v.lng, v.lat] },
properties: { bearing: v.heading, id: v.id },
})),
});
}, 3000);
```
Or use **WebSocket** for true real-time:
```js
const ws = new WebSocket("wss://your-api.com/live");
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
map.getSource("live-vehicle").setData(data);
};
```
---
## Pro Tips
| Feature | How to Add |
| ---------------- | ------------------------------------------ |
| Smooth movement | Use `map.easeTo()` or `flyTo()` |
| Auto-follow | `map.easeTo({ center: coords, zoom: 16 })` |
| Bearing rotation | `"icon-rotate": ["get", "bearing"]` |
| Trail history | Add previous points to a `line` layer |
| Offline fallback | Cache last known position |
---
Real-time location — **beautifully alive**.
---
## Measure Distance on Map
Let users **measure distances** by clicking on the map. Draws a polyline, shows live distance in **kilometers & meters**, and allows removing points by clicking them again.
### Features
- Click to add points
- Click an existing point to remove it
- Real-time distance calculation (Turf.js)
- Clean visual feedback with red markers & line
- Double-click zoom disabled for smooth experience
---
## Full Working Example
```html
Measure Distance
Distance Measurement
Click map to start measuring
```
---
## How It Works
| Action | Result |
| ---------------- | -------------------------------- |
| Click empty area | Adds a red marker |
| Click a marker | Removes that point |
| 2+ points | Draws line & shows live distance |
| New click | Rebuilds entire measurement |
---
Measure anything — **instantly and beautifully**.
---
## Measure Polygon Area
Let users **draw and edit polygons** directly on the map and instantly see the **real-time area** in **square meters**, **hectares**, and **square kilometers** — ideal for land measurement, property boundaries, construction planning, or agriculture.
### Features
- Draw, edit, and delete polygons freely
- Real-time area calculation using **Turf.js**
---
## Full Working Example
```html
Polygon Area Calculator
Polygon Area
Draw a polygon to measure
```
---
## Area Units Explained
| Unit | Conversion | Best For |
| ------------ | -------------------- | ----------------------------- |
| **m²** | Base unit | Small plots, buildings |
| **Hectares** | 1 ha = 10,000 m² | Agriculture, large land |
| **km²** | 1 km² = 1,000,000 m² | City blocks, industrial zones |
---
## Pro Tips
- Use `turf.area()` — most accurate for spherical Earth
- Combine with **snap-to-roads** or **ortho imagery** for surveying
- Export drawn polygon with `draw.getAll()` → send to backend
---
---
## Get Mouse Position
Track the exact geographic coordinates (`lng`, `lat`) of the mouse as it moves or clicks on the map. Perfect for debugging, reverse geocoding, coordinate pickers, or advanced interactions.
---
## Quick Usage
```js
map.on("mousemove", (e) => {
console.log("LngLat:", e.lngLat.lng.toFixed(6), e.lngLat.lat.toFixed(6));
});
map.on("click", (e) => {
console.log("Clicked at:", e.lngLat.toArray());
});
```
`e.lngLat` → `{ lng: 90.123456, lat: 23.123456 }`
`e.point` → `{ x: 512, y: 384 }` (pixel coordinates)
---
## Full Working Example
```html
Get Mouse Position
Mouse Hover Position
Move mouse over map...
Mouse Click Position
Click on map...
```
---
## Event Object Properties
| Property | Type | Description |
| ----------------- | ------------ | ------------------------------- |
| `e.lngLat` | `LngLat` | Geographic coordinates |
| `e.point` | `Point` | Pixel coordinates on map canvas |
| `e.originalEvent` | `MouseEvent` | Raw DOM mouse event |
```js
map.on("mousemove", (e) => {
console.log("Geo:", e.lngLat.toArray());
console.log("Pixel:", e.point);
});
```
---
## Common Use Cases
| Use Case | Code Snippet |
| -------------------------- | --------------------------------------------- |
| Reverse geocoding on click | `fetch('/api/reverse?lng=${lng}&lat=${lat}')` |
| Coordinate picker tool | Save `e.lngLat` to form input |
| Debug map interactions | Log `e.point` for UI testing |
| Draw on click | Use `e.lngLat` to add markers/lines |
---
## Advanced: Copy to Clipboard on Click
```js
map.on("click", (e) => {
const coords = `${e.lngLat.lng.toFixed(6)}, ${e.lngLat.lat.toFixed(6)}`;
navigator.clipboard.writeText(coords);
alert("Coordinates copied: " + coords);
});
```
---
Know exactly where your users are pointing — always!
---
## Add Map Controls
Improve user experience with built-in interactive controls for navigation, fullscreen mode, and scale display.
---
## Available Controls at a Glance
| Control | What It Does | Code |
| -------------- | --------------------------------- | -------------------------------- |
| **Fullscreen** | Expands map to fill entire screen | `new bkoigl.FullscreenControl()` |
| **Navigation** | Zoom in/out buttons + compass | `new bkoigl.NavigationControl()` |
| **Scale** | Shows distance scale bar | `new bkoigl.ScaleControl()` |
---
## Quick Usage
### Fullscreen Control
```js
// Adds a button to toggle fullscreen mode
map.addControl(new bkoigl.FullscreenControl());
```
**What it does:** Adds a button (usually in the top-right corner) that expands the map to fill the entire browser window. Click again to exit fullscreen.
---
### Navigation Control
```js
// Adds zoom buttons (+/-) and a compass
map.addControl(new bkoigl.NavigationControl());
```
**What it does:**
- **Zoom buttons** — Click `+` to zoom in, `-` to zoom out
- **Compass** — Shows map orientation; click to reset to north
---
### Scale Control
```js
// Adds a scale bar showing real-world distance
map.addControl(new bkoigl.ScaleControl());
```
**What it does:** Displays a scale bar that updates as you zoom, showing the real-world distance (e.g., "500m" or "2km").
---
## Full Working Example
```html
Map with Controls
```
---
## Code Breakdown
### 1. Map Initialization
```js
const map = new bkoigl.Map({
container: "map", // HTML element ID
style: "https://map.barikoi.com/styles/barikoi-light/style.json", // Map style
center: [90.3994, 23.7638], // Bangladesh center
zoom: 6, // Country-level zoom
accessToken: "YOUR_BARIKOI_API_KEY", // Your API key
});
```
This creates the base map. Controls are added **after** the map finishes loading.
---
### 2. Wait for Map to Load
```js
map.on("load", () => {
// Add controls here...
});
```
**Why wait for `load`?** The map needs to fully initialize its style, tiles, and sources before you can reliably add controls and layers. Adding controls before `load` may cause issues.
---
### 3. Adding Controls with `addControl()`
```js
map.addControl(new bkoigl.FullscreenControl());
map.addControl(new bkoigl.NavigationControl());
map.addControl(new bkoigl.ScaleControl());
```
The `addControl()` method takes a control instance and optionally a position string.
---
## Positioning Controls
By default, controls are added to the **top-right** corner. You can specify a different position:
```js
// Syntax: map.addControl(control, position)
map.addControl(new bkoigl.NavigationControl(), "top-left");
map.addControl(new bkoigl.FullscreenControl(), "top-right");
map.addControl(new bkoigl.ScaleControl(), "bottom-left");
```
### Available Positions
| Position | Location |
| ---------------- | -------------------------- |
| `"top-left"` | Top-left corner |
| `"top-right"` | Top-right corner (default) |
| `"bottom-left"` | Bottom-left corner |
| `"bottom-right"` | Bottom-right corner |
---
## Control Options
### Navigation Control Options
```js
map.addControl(
new bkoigl.NavigationControl({
showCompass: true, // Show compass (default: true)
showZoom: true, // Show zoom buttons (default: true)
visualizePitch: false, // Show pitch control (default: false)
}),
"top-right"
);
```
### Scale Control Options
```js
map.addControl(
new bkoigl.ScaleControl({
maxWidth: 100, // Maximum width in pixels (default: 100)
unit: "metric", // 'metric', 'imperial', or 'nautical'
}),
"bottom-left"
);
```
---
## Removing Controls
You can remove a control by keeping a reference to it:
```js
const navControl = new bkoigl.NavigationControl();
map.addControl(navControl);
// Later, remove it
map.removeControl(navControl);
```
---
---
## Display a Basic Map
# Your First Barikoi Map
The fastest way to get a beautiful, interactive map on any website or app.
## Full Working Example
```html
My First Barikoi Map
```
---
## Code Breakdown
### 1. Required Dependencies
```html
```
| Resource | Purpose |
| ------------- | --------------------------------------------------------------------- |
| `bkoi-gl.css` | Styles for map UI elements (zoom buttons, attribution, popups, etc.) |
| `bkoi-gl.js` | Core JavaScript library that renders the map and handles interactions |
:::tip Use a specific version in production
Replace `@latest` with a fixed version like `@2.0.4` to avoid unexpected breaking changes.
:::
---
### 2. CSS Setup for Full-Screen Map
```css
body {
margin: 0; /* Remove default browser margins */
padding: 0; /* Remove default browser padding */
}
html,
body,
#map {
height: 100%; /* Map container takes full viewport height */
width: 100%; /* Map container takes full viewport width */
}
```
:::info Why is this necessary?
Without these styles, the map container would have **zero height** and nothing would be visible. The map needs explicit dimensions to render.
:::
---
### 3. Map Container Element
```html
```
This empty `` is where Barikoi GL injects the interactive map canvas. The `id="map"` must match the `container` property in the JavaScript initialization.
---
### 4. Initialize the Map
```js
const map = new bkoigl.Map({
container: "map", // Target element ID
style: "https://map.barikoi.com/styles/barikoi-light/style.json", // Map style URL
center: [90.4071, 23.7925], // [longitude, latitude]
zoom: 11, // Zoom level (0-22)
accessToken: "YOUR_BARIKOI_API_KEY", // Your API key
});
```
#### Configuration Options Explained
| Option | Type | Description |
| ------------- | ------------ | -------------------------------------------------------------------------------- |
| `container` | `string` | The `id` of the HTML element where the map will render |
| `style` | `string` | URL to a Barikoi map style JSON (defines colors, fonts, layers, etc.) |
| `center` | `[lng, lat]` | Initial map center coordinates — **longitude first!** |
| `zoom` | `number` | Initial zoom level: `0` = world view, `22` = building level |
| `accessToken` | `string` | Your Barikoi API key from [developer.barikoi.com](https://developer.barikoi.com) |
:::warning Common Mistake: Coordinate Order
Barikoi GL uses **`[longitude, latitude]`** (like GeoJSON), not `[latitude, longitude]` (like Google Maps). Dhaka is `[90.4071, 23.7925]`, not `[23.7925, 90.4071]`.
:::
---
## Quick Reference: Zoom Levels
| Zoom Level | What You See |
| ---------- | ---------------------- |
| `0-3` | Continents / Countries |
| `4-6` | Large regions |
| `7-10` | Cities |
| `11-14` | Neighborhoods |
| `15-18` | Streets |
| `19-22` | Buildings |
---
---
## All Barikoi Map Styles
# Display Custom Map Style
Pick your vibe. One line of code changes everything.
---
## Official & Community Styles
| Style Name | Preview Mood | Style URL | Best For |
| --------------------- | ----------------------------- | ------------------------------------------------------------- | ---------------------------------- |
| **Barikoi Light** | Clean, professional | `https://map.barikoi.com/styles/barikoi-light/style.json` | Default, dashboards, web apps |
| **Barikoi Dark Mode** | Sleek, modern, night-friendly | `https://map.barikoi.com/styles/barikoi-dark-mode/style.json` | Admin panels, logistics, dark UIs |
| **Barikoi Green** | Fresh, nature-inspired | `https://map.barikoi.com/styles/barkoi_green/style.json` | Eco apps, agriculture, tourism |
| **Planet Map** | Common, realistic | `https://map.barikoi.com/styles/planet_map/style.json` | Real estate, urban planning |
| **OSM Liberty** | OpenStreetMap elegant fork | `https://map.barikoi.com/styles/osm-liberty/style.json` | Open-data lovers, clean minimalism |
:::tip Authentication
All styles require your API key via `?key=YOUR_KEY` in production (or pass `accessToken` in JS).
:::
---
## Full Working Example
```html
Display custom map style
```
---
## Code Breakdown
### 1. Required Dependencies
```html
```
These are the same dependencies used in every Barikoi map. The CSS ensures UI elements render correctly regardless of which style you choose.
---
### 2. Full-Screen Map Container
```css
body,
html,
#map {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
```
This makes the map fill the entire browser window. You can also use fixed dimensions:
```css
#map {
width: 800px;
height: 500px;
}
```
---
### 3. Map Initialization with Custom Style
```js
const map = new bkoigl.Map({
container: "map", // HTML element ID to render the map
center: [90.4071, 23.7925], // Starting position [longitude, latitude]
zoom: 10, // Initial zoom level
accessToken: "YOUR_BARIKOI_API_KEY", // Required for authentication
// 👇 THIS IS THE KEY LINE — change the style URL to switch themes!
style: "https://map.barikoi.com/styles/barikoi-dark-mode/style.json",
});
```
#### The `style` Property Explained
| Property | Description |
| -------------------- | ------------------------------------------------------------------------- |
| **What it is** | A URL pointing to a JSON file that defines every visual aspect of the map |
| **What it controls** | Colors, fonts, layer visibility, road widths, label placement, icons |
| **How to change** | Simply swap the URL to a different style |
---
## Switching Styles Dynamically
You can change the map style **after initialization** using the `setStyle()` method:
```js
// Switch to dark mode on button click
document.getElementById("darkBtn").addEventListener("click", () => {
map.setStyle(
"https://map.barikoi.com/styles/barikoi-dark-mode/style.json?key=YOUR_BARIKOI_API_KEY"
);
});
document.getElementById("lightBtn").addEventListener("click", () => {
map.setStyle(
"https://map.barikoi.com/styles/barikoi-light/style.json?key=YOUR_BARIKOI_API_KEY"
);
});
```
:::warning Note
Calling `setStyle()` will remove any custom layers, markers, or sources you've added. You'll need to re-add them after the style loads using the `style.load` event.
:::
---
## Style Comparison
### When to Use Each Style
| Style | ✅ Best For |
| --------------------- | ------------------------------------ |
| **Barikoi Light** | General purpose, public-facing apps |
| **Barikoi Dark Mode** | Night mode, dashboards, admin panels |
| **Barikoi Green** | Environmental apps, parks, tourism |
| **Planet Map** | Detailed city exploration |
| **OSM Liberty** | Open-source projects, minimal look |
---
---
## Examples
# Interactive Examples
Copy-paste ready code examples to build powerful mapping applications. Each example includes a live demo, full source code, and detailed explanations.
## Getting Started
Perfect for your first Barikoi map integration.
Display a Basic Map
Your first interactive map in under 10 seconds
Custom Map Styles
Light, dark, and custom-themed maps
Map Controls
Zoom, navigation, fullscreen controls
---
## Markers & Popups
Add interactive markers and information windows.
Add a Marker
Drop pins on specific locations
Draggable Marker
Markers users can move around
Click to Add Marker
Interactive marker placement
Popups & Info Windows
Display rich content on markers
Multiple Markers
Efficiently display hundreds of markers
Animated Marker
Eye-catching pulsing effect
---
## Layers & Styling
Work with GeoJSON, lines, polygons, and custom layers.
GeoJSON Lines
Draw routes and paths
Polygons & Areas
Highlight regions and boundaries
Multiple GeoJSON Layers
Complex multi-layer visualizations
Custom Icon Layer
Use your own marker images
Vector Tiles
High-performance tile layers
---
## Advanced Features
Real-time tracking, animations, and interactive tools.
Live Real-Time Data
Track moving objects in real-time
Animate Along Path
Smooth marker animation on routes
Camera Animation
Cinematic map movements
Measure Distance
Calculate distances between points
Measure Area
Calculate polygon area
Click to Center
Interactive map centering
Fly to Location
Smooth animated transitions
Fit Bounds
Auto-zoom to show all markers
Mouse Position
Display cursor coordinates
---
## Need Help?
- [Full API Documentation](/docs/maps-api)
- [Get Your API Key](https://developer.barikoi.com)
- [Community Support](https://barikoi.com/contact)
---
## Add GeoJSON Line
Draw beautiful, smooth lines using GeoJSON `LineString` — ideal for showing routes, roads, walking paths, delivery tracks, or administrative boundaries.
---
## Quick Usage
```js
map.addSource("route", {
type: "geojson",
data: yourLineGeoJSON,
});
map.addLayer({
id: "route-line",
type: "line",
source: "route",
paint: {
"line-color": "#0066ff",
"line-width": 6,
},
layout: {
"line-cap": "round",
"line-join": "round",
},
});
```
---
## Full Working Example
```html
Add GeoJSON Line - Barikoi GL
```
---
## Line Paint Properties
| Property | Recommended Values | Effect |
| ------------------ | -------------------- | ------------------- |
| `"line-color"` | `#0066ff`, `#ef4444` | Line color |
| `"line-width"` | `4–12` | Thickness in pixels |
| `"line-opacity"` | `0.7–1.0` | Transparency |
| `"line-dasharray"` | `[2, 2]`, `[4, 2]` | Dashed line |
| `"line-blur"` | `2–8` | Soft glow effect |
**Pro Tip**: Add a wider, semi-transparent line underneath for a glowing effect!
---
## Line Layout Properties
| Property | Value | Purpose |
| ------------- | --------- | -------------- |
| `"line-cap"` | `"round"` | Smooth ends |
| `"line-join"` | `"round"` | Smooth corners |
Always use `"round"` for natural-looking routes.
---
## Advanced: Animated Dashed Line
```js
let step = 0;
setInterval(() => {
step += 0.5;
map.setPaintProperty("route-line", "line-dasharray", [0, step, 4, step]);
}, 100);
```
Creates a "marching ants" animation — great for live tracking!
---
## Advanced: Gradient Line
```js
map.on("load", () => {
map.addSource("route", {
type: "geojson",
data: route,
lineMetrics: true, // Add this line
});
// Main route line
map.addLayer({
id: "route-line",
type: "line",
source: "route",
layout: {
"line-cap": "round",
"line-join": "round",
},
paint: {
"line-color": [
"interpolate",
["linear"],
["line-progress"],
0,
"#ef4444",
0.5,
"#f59e0b",
1,
"#10b981",
],
"line-width": 8,
"line-opacity": 1,
"line-gradient": [
// Add this property
"interpolate",
["linear"],
["line-progress"],
0,
"#ef4444",
0.5,
"#f59e0b",
1,
"#10b981",
],
},
});
});
```
Requires `line-gradient` and data with `line-progress` (advanced).
---
Draw routes that look **professional** — instantly.
---
## Add Polygon(Layers-and-styling)
Draw **beautiful filled polygons** using GeoJSON — ideal for highlighting delivery zones, flood-prone areas, property boundaries, service coverage, or any custom region.
---
## Quick Usage
```js
map.addSource("zone", {
type: "geojson",
data: yourPolygonGeoJSON,
});
map.addLayer({
id: "zone-fill",
type: "fill",
source: "zone",
paint: {
"fill-color": "#3b82f6",
"fill-opacity": 0.5,
},
});
map.addLayer({
id: "zone-outline",
type: "line",
source: "zone",
paint: {
"line-color": "#1d4ed8",
"line-width": 3,
},
});
```
---
## Full Working Example
```html
Add Polygon - Barikoi GL
Dhaka Service Zone
Delivery available within this highlighted area. Estimated arrival:
30–45 mins.
```
---
## Polygon Paint Properties
| Property | Recommended Values | Effect |
| ---------------------- | -------------------- | --------------------------------- |
| `"fill-color"` | `#3b82f6`, `#10b981` | Fill color |
| `"fill-opacity"` | `0.3–0.7` | Transparency (great for overlays) |
| `"fill-outline-color"` | Auto (or custom) | Border color |
**Pro Tip**: Always add a separate `line` layer for crisp borders!
---
## Advanced: Multiple Polygons (Zones)
```js
const zones = {
type: "FeatureCollection",
features: [
{ geometry: { ... }, properties: { name: "Zone A", color: "#ef4444" } },
{ geometry: { ... }, properties: { name: "Zone B", color: "#f59e0b" } }
]
};
// Then use data-driven styling:
"fill-color": ["get", "color"]
```
---
## Advanced: Click to Highlight
```js
map.on("click", "zone-fill", (e) => {
map.setPaintProperty("zone-fill", "fill-opacity", 0.8);
});
```
---
---
## Icon Layer
Use **`symbol` layers** with custom icons to display **thousands of branded markers** efficiently — far more performant than individual `Marker` objects.
Ideal for store locators, vehicle tracking, custom POIs, or any branded point data.
---
## Quick Usage
```js
map.loadImage("https://your-icon.png").then((image) => {
map.addImage("my-icon", image.data);
map.addLayer({
id: "stores",
type: "symbol",
source: "stores-source",
layout: {
"icon-image": "my-icon",
"icon-size": 0.12,
},
});
});
```
---
## Full Working Example
```html
Add a icon layer
```
---
## Key Layout Properties
| Property | Value Example | Purpose |
| ---------------------- | -------------------------------- | ---------------------------- |
| `"icon-image"` | `"my-icon"` or `["get", "icon"]` | Static or dynamic icon |
| `"icon-size"` | `0.08 – 0.15` | Scale factor (0.1 = normal) |
| `"icon-allow-overlap"` | `true` / `false` | Show even when overlapping |
| `"icon-anchor"` | `"bottom"` | Align icon tip to coordinate |
---
## Performance Comparison
| Method | Max Markers | Best For |
| ----------------------- | ----------- | ------------------------------------- |
| `new bkoigl.Marker()` | ~100 | Few interactive markers |
| **Symbol Layer (this)** | 10,000+ | Large datasets, clustering, filtering |
**Symbol layers are 50x+ faster** than DOM markers.
---
## Advanced: Dynamic Icon from Property
```js
"icon-image": ["concat", ["get", "category"], "-icon"]
```
Works perfectly with data like `{ category: "restaurant" }`
---
---
## Add Multiple GeoJSON Geometries
# Multiple GeoJSON Layers
Add and style multiple geometry types (points, lines, polygons) from a single GeoJSON source with independent layer controls.
## Complete Working Example
```html
Multiple GeoJSON Geometries - Barikoi Maps
```
---
## How It Works
### 1. GeoJSON Source Structure
The example uses a **FeatureCollection** containing multiple features with different geometry types:
```javascript
{
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: { type: "Polygon", coordinates: [...] } // Park boundary
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [...] } // Entrance 1
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [...] } // Entrance 2
}
]
}
```
### 2. Adding the GeoJSON Source
```javascript
map.addSource("national-park", {
type: "geojson",
data: {
/* GeoJSON data here */
},
});
```
| Parameter | Value | Description |
| --------- | ---------------- | ----------------------------------------------- |
| `type` | `"geojson"` | Specifies this is a GeoJSON source |
| `data` | `GeoJSON object` | Contains all features (polygons, points, lines) |
### 3. Creating Separate Layers with Filters
Each layer uses a **filter** to display only specific geometry types:
**Polygon Layer (Park Boundary):**
```javascript
filter: ["==", "$type", "Polygon"];
```
- `$type` is a special expression that checks the geometry type
- Only shows features where geometry type equals "Polygon"
**Circle Layer (Points of Interest):**
```javascript
filter: ["==", "$type", "Point"];
```
- Only shows features where geometry type equals "Point"
---
## Layer Configuration Details
### Polygon Fill Layer
```javascript
{
id: "park-boundary",
type: "fill",
source: "national-park",
paint: {
"fill-color": "#4CAF50", // Solid fill color
"fill-opacity": 0.3, // 30% opacity (semi-transparent)
"fill-outline-color": "#2E7D32" // Border color
},
filter: ["==", "$type", "Polygon"]
}
```
### Circle Layer (Points)
```javascript
{
id: "park-points",
type: "circle",
source: "national-park",
paint: {
"circle-radius": 8, // Size of the circle
"circle-color": "#FF5722", // Fill color
"circle-stroke-width": 2, // Border thickness
"circle-stroke-color": "#FFFFFF" // Border color
},
filter: ["==", "$type", "Point"]
}
```
---
## Advanced Examples
### Add LineString Features (Trails/Roads)
```javascript
// Add line features to your GeoJSON
{
type: "Feature",
geometry: {
type: "LineString",
coordinates: [
[90.399452, 23.780573],
[90.4022, 23.70109],
[90.31403, 23.7279]
]
}
}
// Create a line layer
map.addLayer({
id: "park-trails",
type: "line",
source: "national-park",
paint: {
"line-color": "#2196F3",
"line-width": 3,
"line-dasharray": [2, 2] // Dashed line
},
filter: ["==", "$type", "LineString"]
});
```
### Add Properties for Dynamic Styling
```javascript
{
type: "Feature",
geometry: { type: "Point", coordinates: [...] },
properties: {
"name": "Main Entrance",
"type": "entrance",
"status": "open"
}
}
// Style based on properties
map.addLayer({
id: "smart-points",
type: "circle",
source: "national-park",
paint: {
"circle-color": [
"case",
["==", ["get", "status"], "open"], "#4CAF50",
["==", ["get", "status"], "closed"], "#F44336",
"#9E9E9E" // default color
],
"circle-radius": [
"case",
["==", ["get", "type"], "entrance"], 10,
["==", ["get", "type"], "viewpoint"], 8,
6 // default size
]
}
});
```
---
## Common Use Cases
| Use Case | Geometry Types | Example Applications |
| ------------------------ | ------------------- | ---------------------------------- |
| **Park/Reserve Mapping** | Polygon + Points | Park boundary with entrance points |
| **Transport Networks** | LineString + Points | Roads with bus stops |
| **Building Complexes** | Polygon + Points | Building outlines with amenities |
| **Survey Data** | Points + Polygons | Sample points with area boundaries |
---
## Performance Tips
1. **Single Source vs Multiple Sources**
- ✅ Use single source when features are related (same dataset)
- ✅ Use multiple sources when data comes from different APIs or updates independently
2. **Filter Efficiency**
- Filters are applied during rendering, so all data is still loaded
- For very large datasets, consider splitting into separate sources
3. **Layer Order**
- Layers are drawn in the order they're added
- Add point layers last so they appear on top of polygons/lines
---
## Add Vector Tile Layer
Load **blazing-fast vector tiles** (Mapbox Vector Tiles / MVT format) and style them dynamically. Perfect for rendering millions of features like buildings, village boundaries, land parcels, flood zones, or custom geospatial data.
---
## Quick Usage
```js
map.addSource("my-tiles", {
type: "vector",
url: "https://tiles.example.com/data/{z}/{x}/{y}",
// OR use url: "https://tiles.example.com/data/{z}/{x}/{y}.pbf",
// OR use tiles: ["https://a.tiles.example.com/{z}/{x}/{y}.pbf", ...]
});
map.addLayer({
id: "villages",
type: "fill",
source: "my-tiles",
"source-layer": "villages",
paint: {
"fill-color": "#3b82f6",
"fill-opacity": 0.3,
},
});
```
---
## Full Working Example
```html
Vector Tile Layer - Barikoi GL
Vector Tile Layer
Source: Barikoi building Grid
Format: Vector Tiles (.pbf)
```
---
## Key Concepts
| Term | Meaning |
| ---------------- | ------------------------------------------ |
| `type: "vector"` | Source type for .pbf tiles |
| `tiles: [...]` | Direct tile URLs (no TileJSON needed) |
| `source-layer` | Name of layer inside the .pbf file |
| `filter` | Optional: filter by geometry type or props |
---
## Performance Advantages
| Feature | Vector Tiles | GeoJSON |
| ---------------------- | ------------------------- | ----------------------- |
| File Size | Tiny (~10–50 KB per tile) | Large (MBs) |
| Rendering Speed | Extremely fast | Slow with 10k+ features |
| Zoom-dependent Styling | Yes | Limited |
| Interactivity | Full (hover, click) | Possible but heavy |
**Use vector tiles for anything over 5,000 features.**
---
## Add a Draggable Marker
# Add Draggable Marker
Let users drag a marker to select a location — perfect for address pickers, location selection, and delivery drop-off points.
---
## Quick Usage
```js
// Create a draggable marker
const marker = new bkoigl.Marker({ draggable: true })
.setLngLat([90.364, 23.8237])
.addTo(map);
// Listen for when the user stops dragging
marker.on("dragend", () => {
const lngLat = marker.getLngLat();
console.log(`New position: ${lngLat.lng}, ${lngLat.lat}`);
});
```
---
## Full Working Example
```html
Add a draggable marker
```
---
## Code Breakdown
### 1. Coordinates Display Styling
```css
.coordinates {
background: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */
color: #fff; /* White text */
position: absolute; /* Float above the map */
bottom: 80px; /* Position from bottom */
left: 10px; /* Position from left */
padding: 5px 10px;
font-size: 11px;
line-height: 18px;
border-radius: 3px;
display: none; /* Hidden initially */
width: 200px;
}
```
This creates a floating info box that appears after the user drags the marker. It's positioned in the bottom-left corner of the map.
---
### 2. Create a Draggable Marker
```js
const marker = new bkoigl.Marker({ draggable: true })
.setLngLat([90.36402004477634, 23.823730671721])
.addTo(map);
```
| Part | Description |
| ------------------------ | ---------------------------------------- |
| `{ draggable: true }` | Enables drag functionality on the marker |
| `.setLngLat([lng, lat])` | Sets initial marker position |
| `.addTo(map)` | Adds the marker to the map |
:::tip
When `draggable: true`, the cursor changes to a grab hand when hovering over the marker.
:::
---
### 3. Handle the Drag End Event
```js
function onDragEnd() {
// Get the marker's current position after dragging
const lngLat = marker.getLngLat();
// Show the coordinates display
coordinates.style.display = "block";
// Update the display with new coordinates
coordinates.innerHTML = `Longitude: ${lngLat.lng}Latitude: ${lngLat.lat}`;
}
```
**What `getLngLat()` returns:**
```js
{
lng: 90.36402004477634, // Longitude
lat: 23.823730671721 // Latitude
}
```
---
### 4. Attach the Event Listener
```js
marker.on("dragend", onDragEnd);
```
This registers the `onDragEnd` function to be called whenever the user finishes dragging the marker.
---
## Available Marker Events
| Event | When It Fires |
| ----------- | ------------------------------------------ |
| `dragstart` | User starts dragging the marker |
| `drag` | Continuously while marker is being dragged |
| `dragend` | User releases the marker after dragging |
### Example: Track All Drag Events
```js
marker.on("dragstart", () => {
console.log("Started dragging");
});
marker.on("drag", () => {
const pos = marker.getLngLat();
console.log(`Dragging: ${pos.lng}, ${pos.lat}`);
});
marker.on("dragend", () => {
const pos = marker.getLngLat();
console.log(`Finished at: ${pos.lng}, ${pos.lat}`);
});
```
---
## Practical Use Case: Address Picker
Combine with reverse geocoding to get the address at the marker's location:
```js
marker.on("dragend", async () => {
const { lng, lat } = marker.getLngLat();
// Call Barikoi Reverse Geocode API
const response = await fetch(
`https://barikoi.xyz/v2/api/search/reverse/geocode?longitude=${lng}&latitude=${lat}&api_key=YOUR_API_KEY`
);
const data = await response.json();
console.log("Address:", data.place.address);
});
```
---
---
## Add Markers on Map Click
Let users drop pins anywhere on the map by clicking — perfect for tagging locations, collecting points of interest, or building interactive surveys.
---
## Quick Usage
```js
// Add a marker wherever the user clicks
map.on("click", (e) => {
new bkoigl.Marker()
.setLngLat(e.lngLat) // Use click coordinates
.addTo(map);
});
```
---
## Full Working Example
```html
Add markers on map click
```
---
## Code Breakdown
### 1. Initialize the Map
```js
const map = new bkoigl.Map({
container: "map", // HTML element ID
style: "https://map.barikoi.com/styles/barikoi-light/style.json", // Map style
center: [90.36402004477634, 23.823730671721], // Initial center
zoom: 6, // Starting zoom
accessToken: "YOUR_BARIKOI_API_KEY", // Your API key
});
```
---
### 2. Wait for Map to Load
```js
map.on("load", () => {
// Register events here...
});
```
**Why wait?** The map must fully load its style and tiles before event handlers work reliably. Always register interactive events inside the `load` callback.
---
### 3. Register the Click Event
```js
map.on("click", (e) => {
// This runs every time the user clicks the map
});
```
The `click` event fires whenever the user clicks anywhere on the map. The event object `e` contains useful information about the click.
---
### 4. The Event Object Explained
```js
map.on("click", (e) => {
console.log(e);
});
```
**Key properties of the event object `e`:**
| Property | Type | Description |
| ----------------- | ------------ | -------------------------------------------------- |
| `e.lngLat` | `LngLat` | Geographic coordinates of the click `{ lng, lat }` |
| `e.point` | `Point` | Pixel coordinates on the map canvas `{ x, y }` |
| `e.originalEvent` | `MouseEvent` | The original DOM mouse event |
```js
// Example: Access click coordinates
map.on("click", (e) => {
console.log("Longitude:", e.lngLat.lng);
console.log("Latitude:", e.lngLat.lat);
console.log("Pixel X:", e.point.x);
console.log("Pixel Y:", e.point.y);
});
```
---
### 5. Create a Marker at Click Location
```js
const marker = new bkoigl.Marker({ draggable: true })
.setLngLat(e.lngLat) // Use the click coordinates directly
.addTo(map);
```
| Part | Description |
| ---------------------- | ------------------------------------------- |
| `{ draggable: true }` | Makes the marker moveable after placement |
| `.setLngLat(e.lngLat)` | Positions the marker where the user clicked |
| `.addTo(map)` | Displays the marker on the map |
:::tip
You can pass `e.lngLat` directly to `setLngLat()` — no need to extract `lng` and `lat` separately.
:::
---
## Advanced: Track All Placed Markers
Keep references to all markers so you can manage them later:
```js
const markers = []; // Store all markers
map.on("click", (e) => {
const marker = new bkoigl.Marker({ draggable: true })
.setLngLat(e.lngLat)
.addTo(map);
markers.push(marker); // Add to array
console.log(`Total markers: ${markers.length}`);
});
// Remove all markers
function clearAllMarkers() {
markers.forEach((marker) => marker.remove());
markers.length = 0; // Clear the array
}
```
---
## Advanced: Limit to One Marker
Replace the previous marker when a new one is placed:
```js
let currentMarker = null;
map.on("click", (e) => {
// Remove existing marker if any
if (currentMarker) {
currentMarker.remove();
}
// Create new marker
currentMarker = new bkoigl.Marker({ draggable: true })
.setLngLat(e.lngLat)
.addTo(map);
});
```
---
## Advanced: Show Coordinates on Click
Display the coordinates in a popup when clicking:
```js
map.on("click", (e) => {
const marker = new bkoigl.Marker().setLngLat(e.lngLat).addTo(map);
// Add a popup with coordinates
const popup = new bkoigl.Popup({ offset: 25 }).setHTML(`
Location
Lng: ${e.lngLat.lng.toFixed(6)}
Lat: ${e.lngLat.lat.toFixed(6)}
`);
marker.setPopup(popup).togglePopup();
});
```
---
## Other Map Events
| Event | Description |
| ------------- | --------------------------- |
| `click` | Single click on the map |
| `dblclick` | Double click on the map |
| `contextmenu` | Right-click on the map |
| `mousemove` | Mouse moves over the map |
| `mouseenter` | Mouse enters the map canvas |
| `mouseleave` | Mouse leaves the map canvas |
---
---
## Add a Marker to the Map
# Add a Marker
Drop a pin on any location to highlight points of interest, store locations, or user positions.
---
## Quick Usage
```js
// Create a marker and add it to the map
const marker = new bkoigl.Marker()
.setLngLat([90.364, 23.8237]) // [longitude, latitude]
.addTo(map);
```
That's it! Three lines to place a marker on your map.
---
## Full Working Example
```html
Add a marker
```
---
## Code Breakdown
### 1. Initialize the Map
```js
const map = new bkoigl.Map({
container: "map", // Target HTML element
style: "https://map.barikoi.com/styles/barikoi-light/style.json", // Map style
center: [90.36402004477634, 23.823730671721], // Initial center point
zoom: 6, // Zoom level
accessToken: "YOUR_BARIKOI_API_KEY", // Your API key
});
```
This creates the base map. The `center` coordinates define where the map initially focuses.
---
### 2. Create and Position the Marker
```js
const marker = new bkoigl.Marker()
.setLngLat([90.36402004477634, 23.823730671721])
.addTo(map);
```
| Method | What It Does |
| ------------------------ | ------------------------------------------------------------- |
| `new bkoigl.Marker()` | Creates a new marker instance with default styling (blue pin) |
| `.setLngLat([lng, lat])` | Sets the marker's position — **longitude first!** |
| `.addTo(map)` | Attaches the marker to your map instance |
:::tip Method Chaining
Barikoi GL supports method chaining, so you can create, position, and add a marker in one statement.
:::
---
## Marker Customization Options
### Change Marker Color
```js
const marker = new bkoigl.Marker({ color: "#FF5733" }) // Custom hex color
.setLngLat([90.364, 23.8237])
.addTo(map);
```
### Use a Custom HTML Element
```js
// Create a custom marker element
const el = document.createElement("div");
el.className = "custom-marker";
el.style.backgroundImage = "url(https://example.com/pin.png)";
el.style.width = "32px";
el.style.height = "32px";
const marker = new bkoigl.Marker({ element: el })
.setLngLat([90.364, 23.8237])
.addTo(map);
```
### Marker Options Reference
| Option | Type | Default | Description |
| ----------- | ------------- | ----------- | ----------------------------------------------------------- |
| `color` | `string` | `"#3FB1CE"` | Marker color (hex, rgb, or color name) |
| `element` | `HTMLElement` | `null` | Custom DOM element to use as marker |
| `anchor` | `string` | `"center"` | Position anchor: `center`, `top`, `bottom`, `left`, `right` |
| `offset` | `[x, y]` | `[0, 0]` | Pixel offset from the anchor position |
| `draggable` | `boolean` | `false` | Whether the marker can be dragged |
| `scale` | `number` | `1` | Scale factor for the default marker |
---
## Adding Multiple Markers
```js
const locations = [
{ lng: 90.364, lat: 23.8237, name: "Location A" },
{ lng: 90.4125, lat: 23.8103, name: "Location B" },
{ lng: 90.389, lat: 23.75, name: "Location C" },
];
locations.forEach((loc) => {
new bkoigl.Marker().setLngLat([loc.lng, loc.lat]).addTo(map);
});
```
---
## Removing a Marker
```js
// Keep a reference to remove later
const marker = new bkoigl.Marker().setLngLat([90.364, 23.8237]).addTo(map);
// Remove the marker
marker.remove();
```
---
---
## Add Popup to Marker
Combine custom markers with informative popups to create engaging, interactive map experiences.
## Full Working Example
```html
Popup with Marker
```
---
## Code Breakdown
### 1. Custom Marker Styling
```css
#marker {
background-image: url("..."); /* Your custom image */
background-size: cover; /* Fill the entire marker area */
width: 50px; /* Marker width */
height: 50px; /* Marker height */
border-radius: 50%; /* Make it circular */
cursor: pointer; /* Show clickable cursor on hover */
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); /* Add subtle shadow */
}
```
:::tip Customize Your Marker
Replace the `background-image` URL with any image you want – your logo, a custom icon, or a photo. You can also adjust the size, shape, and styling to match your design.
:::
---
### 2. Popup Styling
```css
.bkoigl-popup {
max-width: 200px; /* Limit popup width for better readability */
}
```
The `.bkoigl-popup` class lets you customize the popup's appearance. You can override default styles to match your brand.
---
### 3. Create the Popup
```js
const popup = new bkoigl.Popup({ offset: 25 }).setText(
"The Jatiya Sangsad Bhaban is the house of the Parliament of Bangladesh..."
);
```
#### Popup Configuration Options
| Option | Type | Description |
| -------- | -------- | ---------------------------------------------------------- |
| `offset` | `number` | Distance (in pixels) between popup and marker anchor point |
#### Popup Content Methods
| Method | Description | Example |
| ------------ | -------------------------------------------- | ------------------------------------------- |
| `.setText()` | Set plain text content | `popup.setText("Hello World")` |
| `.setHTML()` | Set HTML content (images, links, formatting) | `popup.setHTML("Title...")` |
:::info Rich Content with HTML
Use `.setHTML()` instead of `.setText()` to add formatted text, images, links, and other HTML elements to your popup.
:::
---
### 4. Create Custom Marker Element
```js
const el = document.createElement("div");
el.id = "marker";
```
This creates a `` element that will be styled by the CSS `#marker` rule. You can add classes, data attributes, or event listeners to this element.
---
### 5. Attach Popup to Marker
```js
new bkoigl.Marker({ element: el })
.setLngLat(monument) // Position the marker
.setPopup(popup) // Attach the popup
.addTo(map); // Add to map
```
#### Marker Methods Explained
| Method | Description |
| -------------- | ---------------------------------------------------- |
| `.setLngLat()` | Set marker position `[longitude, latitude]` |
| `.setPopup()` | Attach a popup that opens when the marker is clicked |
| `.addTo()` | Add the marker to the map |
:::warning Popup Behavior
By default, popups open when you **click** the marker. Use `popup.options.closeButton = false` to remove the close button, or `popup.options.closeOnClick = false` to prevent closing when clicking the map.
:::
---
## Advanced Popup Examples
### HTML Content with Formatting
```js
const popup = new bkoigl.Popup({ offset: 25 }).setHTML(`
Jatiya Sangsad Bhaban
Designed by Louis Kahn
Learn More →
`);
```
### Multiple Popups on Different Markers
```js
const locations = [
{ coords: [90.378554, 23.762607], name: "Parliament" },
{ coords: [90.406085, 23.810332], name: "Shaheed Minar" },
{ coords: [90.398575, 23.750874], name: "Lalbagh Fort" },
];
locations.forEach((loc) => {
const popup = new bkoigl.Popup({ offset: 25 }).setText(loc.name);
new bkoigl.Marker().setLngLat(loc.coords).setPopup(popup).addTo(map);
});
```
---
## Popup Configuration Options
| Option | Type | Default | Description |
| -------------- | --------- | --------- | -------------------------------------------------- |
| `closeButton` | `boolean` | `true` | Show/hide the close button |
| `closeOnClick` | `boolean` | `true` | Close popup when clicking elsewhere on the map |
| `closeOnMove` | `boolean` | `false` | Close popup when the map moves |
| `offset` | `number` | `0` | Distance between popup and anchor point |
| `anchor` | `string` | auto | Position relative to marker: `top`, `bottom`, etc. |
| `maxWidth` | `string` | `"240px"` | Maximum popup width |
Example with custom options:
```js
const popup = new bkoigl.Popup({
offset: 30,
closeButton: false,
closeOnClick: true,
maxWidth: "300px",
anchor: "bottom",
});
```
---
---
## Multiple Markers as Layer
Render **hundreds or thousands** of markers efficiently using a `symbol` layer with dynamic icons based on categories — far more performant than individual `Marker` objects.
---
## Quick Usage
```js
map.addLayer({
id: "poi-layer",
type: "symbol",
source: "pois",
layout: {
"icon-image": ["get", "icon"], // Dynamic icon from properties
"icon-size": 0.09,
},
});
```
---
## Full Working Example
```html
Multiple Markers as Layer - Barikoi GL
Location Types
Restaurant
Hotel
Shop
Office
Hospital
```
---
## Why Use Symbol Layers?
| Method | Performance | Best For |
| ----------------------- | ------------------ | ------------------------------------- |
| `new bkoigl.Marker()` | Slow (1–100) | Few markers with rich popups |
| **Symbol Layer (this)** | Fast (100–10,000+) | Large datasets, clustering, filtering |
**Symbol layers are 10–50x faster** than individual markers.
---
## Key Layout Properties
| Property | Value Example | Purpose |
| ---------------------- | ----------------- | ------------------------------------ |
| `"icon-image"` | `["get", "icon"]` | Dynamic icon from feature properties |
| `"icon-size"` | `0.09` | Scale (0.05 = small, 0.15 = large) |
| `"icon-allow-overlap"` | `false` | Prevent stacking |
| `"icon-anchor"` | `"bottom"` | Align pin tip to coordinate |
---
## Advanced: Filter by Category
```js
// Show only restaurants
map.setFilter("pois-layer", ["==", ["get", "category"], "restaurant"]);
// Clear filter
map.setFilter("pois-layer", null);
```
---
## Advanced: Dynamic Icon Size by Zoom
```js
"icon-size": [
"interpolate",
["linear"],
["zoom"],
10, 0.06,
15, 0.12
]
```
---
Scale to thousands — effortlessly!
---
## Soft Pulsing Marker
Add a **smooth, animated pulsing dot** that gently breathes and glows — ideal for showing live user location, real-time tracking, alerts, or emphasizing important points.
---
## Quick Usage
```js
map.addImage("pulsing-dot", pulsingDot, { pixelRatio: 2 });
map.addLayer({
id: "pulse-layer",
type: "symbol",
source: "user-location",
layout: { "icon-image": "pulsing-dot" },
});
```
Uses a **dynamic canvas-based image** that animates in real-time.
---
## Full Working Example
```html
Soft Pulsing Marker
```
---
## Customize the Pulse
| Property | Change To | Effect |
| --------------- | -------------------------- | ------------------- |
| Pulse color | `rgba(255, 100, 100, ...)` | Red pulse (alert) |
| Speed | `duration = 800` | Faster pulse |
| Size | `size = 240` | Larger dot |
| Outer radius | `0.9 * t` | Stronger pulse wave |
| Inner dot color | `rgba(255, 100, 100, 1)` | red (Emergency/SOS) |
**Popular Variants**:
- Green pulse → Live user location
- Red pulse → Emergency/SOS
---
## Advanced: Multiple Pulsing Markers
```js
// ...existing code...
features: [
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.39, 23.79],
},
},
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.41, 23.81],
},
},
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.37, 23.77],
},
},
],
// ...existing code...
```
All will pulse in perfect sync!
---
## Pro Tip: Combine with Geolocation
```js
// Add a separate source for the live geolocation marker
map.addSource("pulse-point", {
type: "geojson",
data: {
type: "Feature",
geometry: {
type: "Point",
coordinates: [90.36402004477634, 23.823730671721], // default center
},
},
});
// Add layer for live geolocation marker
map.addLayer({
id: "pulse-point",
type: "symbol",
source: "pulse-point",
layout: {
"icon-image": "pulsing-dot",
},
});
// Watch user's position and update marker
navigator.geolocation.watchPosition(
(pos) => {
map.getSource("pulse-point").setData({
type: "Feature",
geometry: {
type: "Point",
coordinates: [pos.coords.longitude, pos.coords.latitude],
},
});
// Optional: center map on user's location
// map.setCenter([pos.coords.longitude, pos.coords.latitude]);
},
(error) => {
console.error("Geolocation error:", error);
},
{
enableHighAccuracy: true,
maximumAge: 0,
}
);
```
Real-time live location with pulsing effect
---
Make your map **breathe** — beautifully.
---
## API Usage & Pricing Guide
Barikoi API Pricing | Documentation
## Subscription-Based Pricing Overview
How Our Pricing Works
Barikoi offers subscription-based pricing with flexible plans to match your specific needs. Each plan includes:
Daily API call limits - Use these calls across any of our APIs
Request limits - Maximum locations and vehicles based on your plan
Monthly billing - Simple payment cycle with clear usage metrics
Need a custom solution? If our standard plans don't match your requirements, contact us directly to discuss a tailored plan for your business needs.
## Understanding API Calls vs Requests
A single API request doesn't always equal a single API call:
Some API endpoints call multiple internal APIs to deliver comprehensive results
The number of calls depends on the parameters included in your request
Complex operations (like Rupantor API or Routing API) consume multiple calls per request
Example: A request to Rupantor API results in 2 API calls because it internally uses geocode API to operate.
## API Call Count Reference
Below is a detailed breakdown of how many API calls are consumed by each request type and parameter:
Reverse Geocode API
Parameter
Call Count
Parameter
Call Count
Base Count
1
Address
1
District
1
Union
1
Post_code
1
Pouroshova
1
Country
1
Location_type
1
Sub_district
1
Division
1
Area
1
Post_office
1
Thana
1
Bangla
1
Match
1
Reverse Geocoding API (server)
Parameter
Call Count
Parameter
Call Count
Base Count
1
Address
1
District
1
Union
1
Post_code
1
Pouroshova
1
Country
1
Location_type
1
Sub_district
1
Division
1
Area
1
Post_office
1
Thana
1
Bangla
1
Match
1
Geocoding API
Parameter
Call Count
Parameter
Call Count
Base Count
1
All Other Parameters
0
Autocomplete API
Parameter
Call Count
Parameter
Call Count
Base Count
1
All Other Parameters
0
Autocomplete API with city filter
Parameter
Call Count
Parameter
Call Count
Base Count
1
All Other Parameters
0
Post Office autocomplete API
Parameter
Call Count
Parameter
Call Count
Base Count
1
Post_office
1
All Other Parameters
0
Industrial autocomplete API
Parameter
Call Count
Parameter
Call Count
Base Count
1
All Other Parameters
0
Bangla autocomplete API
Parameter
Call Count
Parameter
Call Count
Base Count
1
Bangla
1
All Other Parameters
0
Distance API
Parameter
Call Count
Parameter
Call Count
Base Count
1
All Other Parameters
0
Rupantor geocoder API
Parameter
Call Count
Parameter
Call Count
Base Count
2
District
1
Thana
1
Bangla
1
All Other Parameters
0
Routing API
Parameter
Call Count
Parameter
Call Count
Base Count
2
All Other Parameters
0
Route optimization API
Parameter
Call Count
Parameter
Call Count
Base Count
2
Per additional point
0
Nearest API
Parameter
Call Count
Parameter
Call Count
Base Count
2
All Other Parameters
0
Route Match API
Parameter
Call Count
Parameter
Call Count
Base Count
2
All Other Parameters
0
Map API
Parameter
Call Count
Parameter
Call Count
Base Count
4
All Other Parameters
0
Custom API: 50 paisa per API call.
## Exceeding Call Limits
What happens when you exceed your call limit?
If you go over your daily/monthly call limit:
Your API key will continue to work initially
You'll receive an email notification
For frequent overages, we'll contact you to discuss adjusting your plan
Significant overuse may trigger a hard limit to protect our platform
Note: There is also a permissive per-minute call limit. If exceeded, you'll receive a 429 error (Too Many Requests). Consider delaying requests or upgrading your plan if this occurs regularly.
## Billing Cycle
Your default billing cycle starts from the day your API key is generated and continues for 29 days. This cycle can be adjusted according to your convenience.
API KeyGenerated
BillingPeriod
Renewal(Day 29)
---
## Architect Reviewer
You are an expert software architect focused on maintaining architectural integrity. Review code changes through an architectural lens, ensuring consistency with established patterns.
## Core Expertise
- **Pattern Adherence**: Verify code follows established patterns (MVC, microservices, CQRS, etc.)
- **SOLID Compliance**: Check for principle violations across the codebase
- **Dependency Analysis**: Ensure proper dependency direction, no circular dependencies
- **Abstraction Levels**: Verify appropriate abstraction without over-engineering
- **Scalability**: Identify potential scaling or maintenance issues early
## Review Process
1. Map the change within the overall system architecture
2. Identify architectural boundaries being crossed
3. Check consistency with existing patterns
4. Evaluate impact on modularity and coupling
5. Assess long-term maintainability implications
6. Suggest improvements where needed
## Focus Areas
- **Service Boundaries**: Clear responsibilities and separation of concerns
- **Data Flow**: Coupling between components and data consistency
- **Domain Model**: Consistency with domain-driven design (if applicable)
- **Performance**: Architectural decisions that affect performance at scale
- **Security**: Security boundaries and data validation points
## Output Format
- **Architectural Impact**: High / Medium / Low
- **Pattern Compliance**: Which patterns apply and adherence status
- **Violations**: Specific violations with explanations and file references
- **Recommendations**: Concrete refactoring or design changes
- **Long-Term Implications**: Effects on maintainability and scalability
Good architecture enables change. Flag anything that makes future changes harder.
---
## Code Reviewer
You are a senior code reviewer. Provide constructive, actionable feedback prioritized by severity.
Aim for coverage. Report every bug or risk that could cause incorrect behavior, a test failure, security exposure, data loss, or a misleading result -- including ones you are uncertain about (mark your confidence). Do not filter for "importance"; the severity buckets below do the ranking, so surface a borderline issue as Low rather than dropping it. Only omit pure style or naming preferences with no correctness or maintainability impact.
## Review Process
1. Understand the change scope and intent
2. Check security first (input validation, auth, injection)
3. Verify correctness and error handling
4. Assess performance implications
5. Review maintainability and test coverage
6. Acknowledge what was done well
## Code Quality Checklist
- Logic correctness and edge cases
- Error handling at system boundaries
- Resource management (no leaks)
- Naming clarity and code organization
- Function complexity (cyclomatic < 10)
- Duplication detection
## Security Review
- Input validation and sanitization
- Authentication and authorization checks
- Injection vulnerabilities (SQL, XSS, command)
- Sensitive data handling
- Dependency vulnerabilities (search CVEs when relevant)
## Performance Review
- Algorithm efficiency
- Database query optimization
- Memory and resource usage
- Caching opportunities
- Async patterns and concurrency
## Design Review
- SOLID principles adherence
- Appropriate abstraction levels
- Coupling and cohesion
- DRY without premature abstraction
## Output Format
Categorize findings by severity:
- **Critical**: Security vulnerabilities, data loss risks, correctness bugs
- **High**: Performance issues, missing error handling, design violations
- **Medium**: Maintainability concerns, missing tests, unclear naming
- **Low**: Style suggestions, minor improvements
Be specific -- include file paths, line numbers, and suggested fixes.
---
## Security Auditor
You are a senior security auditor. Conduct thorough, evidence-based security assessments with actionable findings prioritized by risk.
Favor coverage: report every issue you find, including low-severity and Informational ones and ones you are uncertain about (state your confidence). Let the classification below rank findings rather than dropping any as unimportant. Each finding must still be evidence-backed with a clear remediation path.
## Audit Process
1. Define audit scope and applicable compliance frameworks
2. Review security controls and configurations
3. Identify vulnerabilities and compliance gaps
4. Classify findings by severity and exploitability
5. Provide remediation recommendations with priorities
6. Document evidence for all findings
## Vulnerability Assessment
- Application security (OWASP Top 10)
- Input validation and injection flaws
- Authentication and session management
- Access control and authorization
- Cryptographic practices
- Dependency vulnerabilities
- Configuration security
- API security
## Compliance Frameworks
- SOC 2 Type II
- ISO 27001/27002
- HIPAA, PCI DSS, GDPR
- NIST frameworks
- CIS benchmarks
## Key Review Areas
- **Access Control**: User access reviews, privilege analysis, MFA, RBAC
- **Data Security**: Encryption at rest/transit, data classification, retention policies
- **Infrastructure**: Server hardening, network segmentation, patch management
- **Incident Response**: IR plan readiness, detection capabilities, recovery procedures
- **Third-Party Risk**: Vendor security, dependency supply chain, SLA validation
## Finding Classification
- **Critical**: Actively exploitable, immediate remediation required
- **High**: Significant risk, remediate within days
- **Medium**: Moderate risk, remediate within weeks
- **Low**: Minor risk, address in normal development cycle
- **Informational**: Best practice recommendations
## Output Format
- Executive summary with risk posture assessment
- Detailed findings with evidence and reproduction steps
- Remediation roadmap prioritized by risk and effort
- Compliance gap analysis against applicable frameworks
- Quick wins vs. long-term improvements
Maintain objectivity throughout. Every finding must be evidence-backed with a clear remediation path.
---
## Codebase Review Findings
> Generated: 2026-06-03 | Branch: map-playground | Reviewers: Architect, Code, Security
---
## Table of Contents
- [Executive Summary](#executive-summary)
- [Security Auditor: Vulnerability Assessment](#security-auditor-vulnerability-assessment)
- [Architect Reviewer: Module Structure](#architect-reviewer-module-structure)
- [Code Reviewer: src/ Directory](#code-reviewer-src-directory)
- [Priority Action Items](#priority-action-items)
---
## Executive Summary
### Overall Finding Counts
| Source | Critical | High | Medium | Low | Informational | Total |
|--------|----------|------|--------|-----|---------------|-------|
| Security | 0 | 2 | 9 | 7 | 2 | 21 |
| Architect | - | 3 | 6 | 5 | 2 | 16 |
| Code | 5 | 9 | 10 | 6 | - | 30 |
| **Total** | **5** | **14** | **25** | **18** | **4** | **67** |
### Risk Posture
The codebase shows reasonable security awareness in several areas (header masking in code generation, basic input validation, SSR guards). The primary risks stem from:
1. API keys handled entirely client-side with multiple exposure vectors
2. Debug logging left in production leaking sensitive data
3. Monolithic context files (1000+ lines) violating SRP
4. Duplicate code patterns across route layers and utilities
5. Non-functional request cancellation (AbortController disconnected)
### Positive Patterns Observed
- Consistent React Context + useReducer pattern across both playgrounds
- Sensitive header/parameter masking in `codeGenerator.ts`
- Lazy loading via `React.lazy` for playground components
- Good use of `useCallback` throughout hooks
- SSR guard in `MapContainer.tsx` prevents server-side crashes
- Debounced localStorage persistence (300ms) avoids excessive writes
---
## Security Auditor: Vulnerability Assessment
### OWASP Top 10 Mapping
| OWASP Category | Findings |
|----------------|----------|
| A02 Cryptographic Failures | S2, S7, S13, S21 |
| A03 Injection | S5, S19 |
| A04 Insecure Design | S11, S20 |
| A05 Security Misconfiguration | S3, S16, S18 |
| A07 Auth Failures | S1, S6, S14, S15 |
| A08 Data Integrity | S8, S9 |
| A09 Logging Failures | S4 |
| A10 SSRF | S10 |
### HIGH
#### S1. API Key Sent as Query Parameter -- Logged in Server Logs, Browser History, Referrer Headers
- **Files:** `src/services/mapApiService.ts:143-144`, `src/components/ApiPlayground/RequestBuilder/RequestBuilder.tsx:348-353`, `src/hooks/useApiKey.ts:97-118`
- **OWASP:** A07:2021 | **CWE:** CWE-598
- **Issue:** API keys are sent as URL query parameters in all Barikoi API requests. The key appears in browser history, server access logs, Referer headers, proxy logs, and network monitoring tools. Additionally, in `RequestBuilder.tsx` the key is sent THREE times per request (query param + `api_key` header + `x-api-key` header), tripling the exposure surface.
- **Fix:** Work with Barikoi API team to support header-based authentication. In the interim, send the key only once and only to Barikoi domains.
#### S3. No Content-Security-Policy / No Subresource Integrity for CDN Scripts
- **File:** `docusaurus.config.js:99-113`
- **OWASP:** A05:2021 | **CWE:** CWE-353
- **Issue:** MapLibre GL CSS/JS loaded from `unpkg.com` CDN without `integrity` attributes. No CSP headers configured. A compromised CDN could serve malicious scripts.
- **Fix:** Add `integrity` and `crossorigin` attributes to CDN script/link tags, or remove CDN loading entirely and rely on npm package.
### MEDIUM
#### S2. Base64 Encoding Mistaken for Encryption -- False Sense of Security
- **Files:** `src/store/playground-context.tsx:4-35`, `src/store/map-playground-context.tsx:6-18`
- **OWASP:** A02:2021 | **CWE:** CWE-312
- **Issue:** `encodeValue`/`decodeValue` use `btoa(encodeURIComponent(value))`. The comment says "Simple obfuscation for localStorage to avoid plaintext key storage" but base64 is trivially reversible via `decodeURIComponent(atob())`. This gives developers a false sense of security.
- **Fix:** Either use proper encryption (Web Crypto API) or remove the encoding and be explicit that keys are stored in plaintext.
#### S4. Excessive console.log in Production Code -- Information Disclosure
- **Files:**
- `src/services/mapApiService.ts:219-227`
- `src/services/schemaParser.ts:85, 118, 167-170, 215, 234, 249, 266-267`
- `src/components/ApiPlayground/RequestBuilder/RequestBuilder.tsx:714-719`
- `src/components/MapPlayground/MapInteractions.tsx:47, 102, 111, 120-121`
- `src/components/MapPlayground/MapContainer/MapContainer.tsx:85`
- **OWASP:** A09:2021
- **Issue:** Debug `console.log` statements dump raw API responses (containing API keys in query params), endpoint configurations, and internal state to the browser console in production. In RequestBuilder, logs are inside JSX and execute on every render.
- **Fix:** Remove all `console.log`/`console.warn` statements or replace with a configurable logger disabled in production.
#### S5. Dynamic Method Invocation via `(this as any)[methodName]` in CodeGenerator
- **File:** `src/services/codeGenerator.ts:373`
- **OWASP:** A03:2021 | **CWE:** CWE-470
- **Issue:** `(this as any)[methodName](config)` bypasses TypeScript's type system and could invoke unintended methods if `methodName` is derived from user-controlled data.
- **Fix:** Use an explicit method mapping (switch statement or Record).
#### S7. API Key Exported as Plaintext JSON Without Encryption
- **File:** `src/hooks/useApiKey.ts:148-169`
- **OWASP:** A02:2021 | **CWE:** CWE-312
- **Issue:** `exportApiKeys` creates a JSON file containing raw API keys. If the file is shared or committed, keys are compromised.
- **Fix:** Add a prominent warning before export. Ideally encrypt with a user-provided password.
#### S8. Unsafe Parsing of Imported JSON Without Schema Validation
- **File:** `src/hooks/useApiKey.ts:174-206`
- **OWASP:** A08:2021 | **CWE:** CWE-502
- **Issue:** `importApiKeys` parses JSON and iterates entries without validating each key object's structure. No check that `keyData` is an object or that `keyData.key`/`keyData.name` are strings before use.
- **Fix:** Add schema validation:
```typescript
if (!keyData || typeof keyData !== 'object') continue;
if (typeof keyData.key !== 'string' || typeof keyData.name !== 'string') continue;
```
#### S9. Unvalidated localStorage Data Parsed into Application State
- **Files:** `src/store/playground-context.tsx:286-294`, `src/store/map-playground-context.tsx:793-817`
- **OWASP:** A08:2021 | **CWE:** CWE-502
- **Issue:** Both context providers parse JSON from localStorage directly into state without validating structure. A malicious browser extension or XSS payload could modify localStorage to inject arbitrary data.
- **Fix:** Validate parsed JSON data before dispatching into state using type-narrowing checks.
#### S10. Open Redirect / SSRF Potential via User-Controlled URL
- **Files:** `src/services/apiClient.ts:117-138`, `src/components/ApiPlayground/RequestBuilder/RequestBuilder.tsx:474-478`
- **OWASP:** A10:2021 | **CWE:** CWE-918
- **Issue:** The API client accepts arbitrary URLs with no domain restriction. Users could send their API key to an attacker-controlled server or make requests to internal network resources.
- **Fix:** Add an allowlist of permitted domains (e.g., `*.barikoi.xyz`, `*.barikoi.com`). At minimum, do not attach API keys to non-Barikoi requests.
#### S14. API Key Unencoded in URL String Concatenation
- **File:** `src/hooks/useApiKey.ts:97-118`
- **OWASP:** A07:2021 | **CWE:** CWE-598
- **Issue:** `testApiKey` concatenates the API key directly into a URL string without encoding. Special characters in the key could break the URL or cause injection.
```typescript
const response = await fetch('https://barikoi.xyz/v2/api/search/reverse/geocode?api_key=' + key + '&longitude=...');
```
- **Fix:** Use `URLSearchParams` to properly encode the API key.
#### S15. API Key Attached to Non-Barikoi Requests
- **File:** `src/components/ApiPlayground/RequestBuilder/RequestBuilder.tsx:338-361`
- **OWASP:** A07:2021 | **CWE:** CWE-201
- **Issue:** API key is attached to ALL outgoing requests regardless of target URL. If a user changes the URL to a third-party endpoint, the key is leaked.
- **Fix:** Only attach API key to requests targeting Barikoi domains.
### LOW
#### S6. API Key Input Field Uses type="text" -- Visible on Screen
- **Files:** `src/components/MapPlayground/MapContainer/MapContainer.tsx:269-279`, `src/components/ApiPlayground/RequestBuilder/RequestBuilder.tsx:554-559`
- **Issue:** API key input uses `type="text"`, making the key visible to anyone looking at the screen.
- **Fix:** Change to `type="password"` or use `` with a visibility toggle.
#### S11. No Client-Side Rate Limiting
- **File:** `src/services/apiClient.ts:228-262`
- **Issue:** `RequestQueue` deduplicates concurrent requests but has no rate limiting. Users could rapidly exhaust their API quota.
- **Fix:** Implement client-side rate limiting (e.g., max 10 requests per minute).
#### S12. Weak ID Generation Using Math.random()
- **Files:** `src/store/playground-context.tsx:327`, `src/store/map-playground-context.tsx:867, 884`
- **Issue:** `Math.random()` is not cryptographically secure. Acceptable for UI element IDs, but noted for awareness.
- **Fix:** If IDs are ever used for security purposes, switch to `crypto.randomUUID()`.
#### S13. API Key Stored in Plaintext in React State
- **Files:** `src/store/map-playground-context.tsx:485`, `src/store/playground-context.tsx:95-96`
- **Issue:** API key is accessible via React DevTools. Largely inherent to client-side apps.
- **Fix:** Ensure React DevTools are stripped in production builds.
#### S16. Debug Information Exposed in Production UI
- **File:** `src/components/ApiPlayground/RequestBuilder/RequestBuilder.tsx:992-1006`
- **Issue:** A `` section renders internal OpenAPI schema structure (operationId, requestBody, bodyParameters) visible to all users.
- **Fix:** Gate behind `process.env.NODE_ENV === 'development'`.
#### S19. Direct DOM Manipulation Creates Potential XSS Vector
- **File:** `src/components/MapPlayground/MapInteractions.tsx:50-79, 214-238`
- **Issue:** Uses `document.createElement` and `document.body.appendChild`. While `textContent` is used (safe), direct DOM manipulation bypasses React's virtual DOM.
- **Fix:** Use React portals for toast notifications.
#### S21. Incomplete Sensitive Header Masking List
- **File:** `src/services/codeGenerator.ts:15`
- **Issue:** `SENSITIVE_HEADERS` list missing `'api_key'` (with underscore), `'bearer'`, `'token'`, `'password'`, `'access_token'`, `'refresh_token'`. The Barikoi API uses `api_key` as a query parameter which will NOT be masked in generated code snippets.
- **Fix:** Expand the list to include `'api_key'` and other common auth header/param names.
### INFORMATIONAL
#### S17. Incorrect GitHub Organization Name in Docusaurus Config
- **File:** `docusaurus.config.js:25-26`
- **Issue:** `organizationName: "facebook"` and `projectName: "docusaurus"` are leftover from the Docusaurus template.
- **Fix:** Update to correct Barikoi organization name.
#### S18. No Subresource Integrity for Algolia DocSearch Scripts
- **File:** `docusaurus.config.js:426-431`
- **Issue:** External Algolia scripts loaded without SRI. The Algolia `apiKey` shown is a search-only public key (acceptable).
- **Fix:** Verify Algolia DocSearch uses SRI for loaded scripts.
### Compliance Gap Analysis
#### SOC 2 Type II
| Control Area | Status | Notes |
|---|---|---|
| CC6.1 Logical Access | PARTIAL | API key input should use password field. No MFA on stored keys. |
| CC6.6 Data Transmission | GAP | API keys sent as query parameters. No TLS enforcement at application level. |
| CC7.1 System Monitoring | GAP | No client-side error tracking. Excessive console logging is not structured monitoring. |
#### ISO 27001/27002
| Control | Status | Notes |
|---|---|---|
| A.8.2 Information Classification | GAP | No classification of API keys as sensitive data in the UI. |
| A.10.1 Cryptographic Controls | GAP | Base64 encoding is not encryption. Exported keys are plaintext. |
| A.14.1 Secure Development | PARTIAL | Good: input validation, header masking. Bad: debug info in prod, no CSP. |
---
## Architect Reviewer: Module Structure
**Architectural Impact: Medium**
### Pattern Compliance
| Pattern | Status | Notes |
|---------|--------|-------|
| Docusaurus multi-instance docs | Well-applied | 10+ doc plugin instances for SDKs, examples, migration |
| React Context + useReducer | Applied | Two independent context stores for playgrounds |
| Service layer pattern | Applied | `apiClient.ts` -> `mapApiService.ts` layering |
| Component decomposition | Partial | MapPlayground components decomposed but with duplication |
### A1. Monolithic Context/State File (High)
- **File:** `src/store/map-playground-context.tsx` (1028 lines)
- **Issue:** Mixes 13+ response type interfaces, 10+ domain model interfaces, state shape, 49 action types, reducer logic, context wiring, localStorage persistence, and 40+ action creators in one file. Violates Single Responsibility Principle with 6+ distinct concerns.
- **Also affects:** `src/services/mapApiService.ts` (1197 lines) handles 10+ API endpoints with significant method duplication.
- **Fix:** Split into:
- `src/store/map-playground/types.ts` (all interfaces and type definitions)
- `src/store/map-playground/reducer.ts` (reducer + initial state)
- `src/store/map-playground/actions.ts` (action creators)
- `src/store/map-playground/persistence.ts` (localStorage helpers)
- `src/store/map-playground-context.tsx` (slim provider + hook only)
### A2. Circular-Type Dependency Between Store and Services (High)
- **Files:** `src/services/mapApiService.ts:7`, `src/services/apiClient.ts:1`, `src/services/codeGenerator.ts:1`, `src/store/playground-context.tsx:2`
- **Issue:** Services import types from store. Store imports from services (`OpenAPIEndpoint` from `schemaParser`). This bidirectional dependency is masked by dynamic `await import()` calls in the provider (`map-playground-context.tsx:797-802, 856-858`) and components (`MapInteractions.tsx:100`).
- **Fix:** Create `src/types/` directory with shared interfaces. Both store and services should import from this shared types layer.
### A3. Duplicate MapLibre GL JS Loading (High)
- **Files:** `docusaurus.config.js:99-113`, `package.json`
- **Issue:** CDN loads MapLibre GL JS v3.6.2 via `headTags`. npm installs a different version. `@vis.gl/react-maplibre` manages its own MapLibre dependency. Three potential sources of the same library in the same runtime.
- **Fix:** Remove CDN `headTags` entries. Let `@vis.gl/react-maplibre` manage MapLibre dependency.
### A4. Five Nearly Identical Route Layer Components (High)
- **Files:** `src/components/MapPlayground/MapContainer/RouteLayer.tsx`, `RouteMatchLayer.tsx`, `DetailedRouteLayer.tsx`, `RouteOptimizationLayer.tsx`, `RouteLocationOptimizedLayer.tsx`
- **Issue:** All five follow an identical pattern: get map, get state, extract coordinates, remove old source/layers, add GeoJSON source, add casing layer, add main line layer, return cleanup. Only differences are source/layer ID constants, state field, and line colors.
- **Fix:** Create a generic `RouteLayerBase` component or `useRouteLayer` hook:
```typescript
interface RouteLayerConfig {
sourceId: string;
lineLayerId: string;
casingLayerId: string;
coordinates: [number, number][] | null | undefined;
lineColor: string;
casingColor: string;
fitBounds?: { bbox: number[]; padding: number };
}
```
### A5. GeocodingControls.tsx God Component (High)
- **File:** `src/components/MapPlayground/GeocodingControls.tsx` (2264 lines)
- **Issue:** Single component handles multiple distinct responsibilities: reverse geocode form, autocomplete form, forward geocode form, route forms, route match form, detailed route form, route optimization form, place search, snap to road, and all their input handling, validation, and API calling logic.
- **Fix:** Split into feature-based subcomponents:
```
MapPlayground/GeocodingControls/
GeocodingControls.tsx (orchestrator/layout only)
ReverseGeocodeForm.tsx
AutocompleteForm.tsx
ForwardGeocodeForm.tsx
RoutingForm.tsx
...
```
### A6. Duplicate localStorage Utility Functions (Medium)
- **Files:** `src/store/playground-context.tsx:5-35`, `src/store/map-playground-context.tsx:4-30`
- **Issue:** Both files define identical `encodeValue`, `decodeValue`, `safeGetItem`, `safeSetItem` functions with only the `STORAGE_PREFIX` differing (`'bkoi_'` vs `'bkoi_map_'`). DRY violation.
- **Fix:** Extract to `src/utils/storage.ts` accepting prefix as a parameter.
### A7. Duplicate Error Filtering Logic -- Inconsistent Patterns (Medium)
- **Files:** `src/components/MapPlayground/MapContainer/MapContainer.tsx:59-81`, `src/components/MapPlayground/MapPlayground.tsx:74-98`
- **Issue:** Both implement `isInternalMapError`/`shouldShowError` with nearly identical but slightly divergent `internalErrorPatterns` lists. MapContainer includes `'network'` but MapPlayground does not; MapPlayground uses `'style issue'` while MapContainer uses `'style'`.
- **Fix:** Create shared `src/utils/mapErrors.ts` with a single `isInternalMapError` function.
### A8. API Key State Duplication Between Store and Service (Medium)
- **Files:** `src/store/map-playground-context.tsx:852-862, 797-802`, `src/services/mapApiService.ts:104-106`
- **Issue:** API key is stored in both React context state (`state.apiKey`) and `MapApiService` singleton instance (`this.apiKey`). Manual sync via dynamic imports creates dual-source-of-truth.
- **Fix:** Pass the API key as a parameter to each service method call instead of maintaining it as service state.
### A9. Actions Object Not Memoized (Medium)
- **Files:** `src/store/map-playground-context.tsx:848-1012`, `src/store/playground-context.tsx:321-372`
- **Issue:** `actions` object created as plain object literal in provider body. New reference every render causes all context consumers to re-render unnecessarily.
- **Fix:** Wrap with `useMemo(() => ({ ... }), [])` since `dispatch` from `useReducer` is stable.
### A10. OpenAPI Spec Files Misplaced in src/ (Medium)
- **Files:** `src/business_api.yaml`, `src/business_api_v1.yaml`
- **Issue:** OpenAPI spec files are configuration/data, not source code.
- **Fix:** Move to `static/api-specs/` and update `docusaurus.config.js` paths.
### A11. Content Duplication in static/ (Medium)
- **Issue:** `static/docs/` mirrors `docs/`, `static/examples/` mirrors `examples/`, etc. Agent markdown files duplicated in `static/`.
- **Fix:** Ensure `scripts/copy-to-static.js` only copies what is needed for production. Remove stale duplicates from version control.
### A12. Navbar Duplicate Entry (Low)
- **File:** `docusaurus.config.js:504-507`
- **Issue:** Standalone "Flutter" navbar item duplicates the "Flutter" entry inside "Mobile Development" dropdown.
- **Fix:** Remove the standalone Flutter entry.
### A13. Shared Sidebar Config (Low)
- **File:** `docusaurus.config.js`
- **Issue:** 8 of 10 doc plugin instances reference the same `sidebars.js`. Implicit coupling.
- **Fix:** Create dedicated sidebar files or document the shared dependency.
### A14. Empty Response File (Low)
- **File:** `src/responses/.yaml`
- **Issue:** File with a dot-prefixed name, likely empty or a placeholder.
- **Fix:** Remove or properly name the file.
### A15. Mixed Styling Approaches (Low)
- **Files:** `src/components/MapPlayground/MapPlayground.tsx:257-309`, `src/components/MapPlayground/MarkerManager.tsx:421-453`, `src/components/MapPlayground/MapInteractions.tsx:52-78`
- **Issue:** Mix of CSS modules, inline `