
Geolocation Mapbox
- Atul
- Geolocation
- December 25, 2024
Table of Contents
Mapbox
I do need appreciation for this, I took 2 screenshots and align them into one.
From what I’ve seen there are 2 major Map based development projects which are further based on Openstreetmaps and supports JS:
- Leaflet
- Mapbox
Here Mapbox seems a new shiny toy to play with. As the image above shows there are humangous things in Mapbox. It’s an complete organization which bills you for using their APIs.
So let’s get started with Mapbox.
Well before getting started with Mapbox, I did some watching and got to know interesting things about Mapbox.
Mapbox vs MapLibre
- Mapbox used to be completely open sourced till 2020.
- Mapbox changed their TOS and closed their sources.
- Mapbox made it mandetory to use access token to render a map from Mapbox.
- Community choose to take the Mapbox till 2020 and spin up their own version called Maplibre
- Maplibre is almost similar to Mapbox but it is community build.
- Mapbox on the other hand has financial support to create features.
- From what I can see, Maplibre can be used for basic to advanced stuff.
- Mapbox has everything.
For me as I am new to this Maps relm, I’ll explore Mapbox first because it has kind of given many things out of the box. So just to get the understanding I’ll use Mapbox then I’ll check Maplibre.
I mean, I can use Geocoding of Mapbox easily(as it seems in their tutorials) instead of checking the Geocoding through Maplibre which further digging in lead to another thing called Proton Server. This gets little complicated, I don’t even know what all the half of the things here. So, Mapbox it is for now.
Later on I’ll love to know more about Maplibre!! <3
Getting started with Mapbox
Create an account and get an access token.
For starters this repo here seems quite latest so clone it.
I guess this can be called Hello Earth! Project.
Dependencies
"dependencies": {
"mapbox-gl": "^3.7.0",
"next": "14.2.15",
"react": "^18",
"react-dom": "^18"
},
- Install packages:
yarn
//or
npm i
- Create env file and save as
NEXT_PUBLIC_MAPBOX_API_KEY=""
Well, I didn’t know that!!
sk.jibrish is secret key and pk.gibrish is public.
Get the public one.
Cool isn’t it?
You can find this repo here.
Hello Earth where am I?
"use client";
import { useEffect, useRef, useState } from "react";
interface LocationData {
accuracy: number | null;
altitude: number | null;
altitudeAccuracy: number | null;
heading: number | null;
latitude: number | null;
longitude: number | null;
speed: number | null;
timestamp: number | null;
}
interface GeolocationState {
loading: boolean;
error?: GeolocationPositionError;
}
export default function MyLocation() {
const [isTracking, setIsTracking] = useState<boolean>(false);
const [locationData, setLocationData] = useState<LocationData | null>(null);
const [geoState, setGeoState] = useState<GeolocationState>({
loading: false,
});
const watchId = useRef<number | null>(null);
const startTracking = () => {
if (!("geolocation" in navigator)) {
setGeoState((prev) => ({
...prev,
error: {
code: 0,
message: "Geolocation not supported",
PERMISSION_DENIED: 1,
POSITION_UNAVAILABLE: 2,
TIMEOUT: 3,
},
}));
return;
}
setGeoState((prev) => ({ ...prev, loading: true }));
watchId.current = navigator.geolocation.watchPosition(
(position) => {
setLocationData({
accuracy: position.coords.accuracy,
altitude: position.coords.altitude,
altitudeAccuracy: position.coords.altitudeAccuracy,
heading: position.coords.heading,
latitude: position.coords.latitude,
longitude: position.coords.longitude,
speed: position.coords.speed,
timestamp: position.timestamp,
});
setGeoState((prev) => ({ ...prev, loading: false }));
},
(error) => {
setGeoState((prev) => ({ ...prev, loading: false, error }));
setIsTracking(false);
},
{
enableHighAccuracy: true,
maximumAge: 1000,
timeout: 12000,
}
);
};
useEffect(() => {
console.log("Location data updated:", locationData);
}, [locationData]);
const stopTracking = () => {
if (watchId.current !== null) {
navigator.geolocation.clearWatch(watchId.current);
watchId.current = null;
}
setLocationData(null);
setGeoState((prev) => ({
...prev,
loading: false,
error: undefined,
}));
};
const handleToggle = () => {
if (!isTracking) {
startTracking();
} else {
stopTracking();
}
setIsTracking(!isTracking);
};
// Cleanup on unmount
useEffect(() => {
return () => {
if (watchId.current !== null) {
navigator.geolocation.clearWatch(watchId.current);
}
};
}, []);
return (
<div className=" bg-white rounded-lg shadow-md">
{/* Header */}
<div className="px-4 py-4 border-b border-gray-200 flex items-center justify-between">
<h2 className="text-lg font-bold text-gray-900">Location Tracker</h2>
{/* Custom Toggle Switch */}
<div className="flex items-center space-x-2">
<span className="text-sm text-gray-500">
{isTracking ? "Tracking On" : "Tracking Off"}
</span>
<button
onClick={handleToggle}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 ${
isTracking ? "bg-blue-600" : "bg-gray-200"
}`}
aria-label="Toggle location tracking"
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
isTracking ? "translate-x-6" : "translate-x-1"
}`}
/>
</button>
</div>
</div>
{/* Content */}
<div className="p-6">
{geoState.error && (
<div className="p-4 mb-4 text-red-500 bg-red-50 rounded">
Error: {geoState.error.message}
</div>
)}
{isTracking && geoState.loading && (
<div className="p-4 text-gray-500">Fetching location data...</div>
)}
{locationData && !geoState.loading && (
<div className="p-4 rounded font-mono text-sm text-gray-500 overflow-auto">
{locationData.latitude} , {locationData.longitude}
</div>
)}
{!isTracking && (
<div className="p-2 text-gray-500 text-center">
Toggle the switch to start tracking your location
</div>
)}
</div>
</div>
);
}
// User sees the component with tracking OFF
// User clicks toggle → handleToggle() is called → startTracking() runs
// Browser shows permission prompt
// If allowed, location updates start flowing through the watcher
// Data is formatted and displayed
// When toggle is clicked again, everything is cleaned up
import MyLocation from "@/components/MyLocation";
import MyMapboxMap from "@/components/MyMapboxMap";
export default function Home() {
return (
<div className="flex flex-col items-center w-full relative">
<div className="absolute z-10 ">
<MyLocation />
</div>
<div className="w-full">
<MyMapboxMap />
</div>
</div>
);
}
This is how it should look like!
Now I have added data passing between these two (MyLocation, MyMapBoxMap)
These changes can be seen here.
The running project can be accessed here.