Free IoT-based GPS Tracking Map for ESP32, NodeMCU, and Arduino

Published  January 23, 2025   1
GeoLinker Free IoT-based GPS Tracking Map

In the present world of interconnected devices, there is a lot happening in IoT. Tracking assets, monitoring movement, and analyzing routes are some of the most essential tasks, and they have never been easier, at least on the front end. Using GPS in our projects adds a cool factor, but only when paired with a proper and intuitive UI.

However, the real challenge lies behind the scenes, in the backend infrastructure of the UI and its maintenance. Developing a robust system for capturing, storing, and visualizing GPS data is not always feasible for every individual or organization. An ideal solution would be an API that manages all the visualization tasks, where we only need to send the coordinates, and the API itself stores the data in the database and plots the coordinates.

This is where the CircuitDigest Cloud GeoLinker API, developed by us, becomes the perfect tool, offering a free and ready-to-use GPS tracking solution that simplifies location data handling without the usual complexity.

GeoLinker combines backend efficiency with an intuitive frontend, providing real-time GPS tracking and visual analytics through an easy-to-navigate interface. Here's a snapshot of its key features:

⇒ Live location plotting on interactive maps with automatic route tracing

⇒ Real-time analytics showing distance traveled, average speed, and travel time

⇒ Auto-refreshing dashboard for uninterrupted monitoring

⇒ Multi-device support to track several GPS units at once

⇒ Data history logging with timestamps and accurate coordinates

⇒ Export options for reports or further analysis

⇒ Dual-view UI – Map view + Tabular data for flexible exploration

⇒ Professional dashboard with sortable columns, filters, and map tools

By seamlessly collecting geo-coordinates, storing them efficiently, and presenting them through a feature-rich, user-friendly platform, GeoLinker bridges the gap between technical capability and ease of use, bringing professional-grade GPS tracking within everyone's reach.

What is CircuitDigest Cloud?

Circuit Digest Cloud Features

CircuitDigest Cloud is an initiative aimed at empowering engineers, makers, and hobbyists with essential tools for rapid prototyping. In addition to the GeoLinker API, we also offer other useful APIs, such as QR Code Scanning, License Plate Recognition and SMS API, for those interested in expanding their projects. 

How to use GeoLinker to Visualize GPS Maps?

To use GeoLinker, the first thing you need is an API Key for authentication, which can be obtained while registering an account at CircuitDigest.Cloud. First let's create an account.

Registering a New Account in Circuit Digest Cloud

GeoLinker-Login-UI

Step 1: Visit the Circuit Digest.Cloud Home Page. Click the "Login" button located at the top right corner to be redirected to the login page.

Step 2: If you already have an account, log in using your existing credentials. If not, go to the registration page to create an account by filling in the required details. Once completed, click "Register Now" to sign up.

Step 3: After registering, use your email ID and password to log in on the login page. Next, Step is to generate the API Key.

Generating API Key

Generating Geolinker API Key

Step 4: Once logged in, click on "My Account" at the top right corner.

Step 5: You will be directed to a page where you can generate your API Key. Enter the captcha text in the provided box, then click the "Generate API Key" button

Step 6: If the captcha is correct, you'll see a table displaying your API Key along with its expiration date and usage count. By pressing the copy icon near the API Key, your key gets instantly copied to the clipboard.

NOTE: Currently, there is a limit of 100 uses per key for SMS & ANPR APIs and 10,000 data points for GPS tracking. Once you reach this limit, you can generate another key, which provides an additional 100 uses.
This usage limit is in place to prevent server overload.

API EndPoint

ParameterValue
Base URLhttps://www.circuitdigest.cloud/geolinker
HTTP MethodPOST
Content-Typeapplication/json

Headers

Authorization

  • Type: String

  • Required: Yes

  • Description: API key for secure access (e.g., "rB007iKt0soY")

Content-Type

  • Type: String

  • Required: Yes

  • Description: Must be "application/json"

Request Body Structure

{
  "device_id": "string",
  "timestamp": ["timestamp1", "timestamp2", ...],
  "lat": [lat1, lat2, ...],
  "long": [long1, long2, ...],
  "battery": [battery_level] (optional),
  "payload": [
    {
      "custom_key1": value1,
      "custom_key2": value2
    }
  ] (optional)
}

Required Fields

- device_id: String identifier for the device sending data

- timestamp: Array of timestamp strings in format "YYYY-MM-DD HH:MM:SS"

- lat: Array of latitude coordinates (float values)

- long: Array of longitude coordinates (float values)

Optional Fields

- battery: Array containing battery level percentage (0-100)

- payload: Array of custom key-value pairs for additional sensor data

Data Types and Validation

FieldTypeFormatRangeDescription
timestampArray of strings"YYYY-MM-DD HH:MM:SS

(e.g., "2025-01-09 12:30:45")
-UTC timestamps with configurable timezone offset applied
lat (Latitude)Array of floatsDecimal degrees (e.g., 12.971598) 
 
-90.0 to +90.0Latitude coordinates converted from NMEA format
long (Longitude)Array of floatsDecimal degrees (e.g., 77.594566)-180.0 to +180.0Longitude coordinates converted from NMEA format
battery (Optional)Array of integers-0-100Battery level percentage
payload (Optional)Array of objects--Custom sensor data as key-value pairs

Example Request

{
 "device_id": "GPS_TRACKER_001",
 "timestamp": ["2025-01-09 12:30:45", "2025-01-09 12:31:45"],
 "lat": [12.971598, 12.972341],
 "long": [77.594566, 77.595123],
 "battery": [85],
 "payload": [
   {
     "temperature": 25.6,
     "humidity": 65.2,
     "speed": 45.8
   }
 ]
}

Response Codes

Success Responses
CodeStatusDescription
200OKData successfully received and processed
201CreatedNew device record created and data inserted
 
Error Responses
400Bad RequestInvalid payload format or missing required fields
401UnauthorizedInvalid or missing API key
500Internal Server ErrorServer-side processing error

Validation Rules

  1. Required Fields: All core fields (device_id, timestamp, lat, long) must be present

  2. Array Length Consistency: timestamp, lat, and long arrays must have the same length

  3. Coordinate Bounds: Latitude must be between -90 and +90, longitude between -180 and +180

  4. API Key: A Valid authorization header must be provided

  5. Data Format: Coordinates should not be 0.0, 0.0 (considered invalid GPS fix)

Using GeoLinker

Let’s take an overview of how to use GeoLinker.

Successful Data Upload to GeoLinker

⇒ Data Uploading

Using the API details mentioned above, you can send data to GeoLinker. The data will be plotted, routed, and marked with timestamps, with lots of features added.

GeoLinker-Tracker-Interface

⇒ Data Visualizing

For visualization, simply log in to your Circuit Digest Cloud ID and open the GeoLinker UI using the Track button provided in the GeoLinker card on the homepage.

Initially, you may see the message "No GPS Found"—this means you need to refresh the page. This is mandatory for the first data point to appear. After that, the page will auto-refresh.

Then, you will be able to see the plotted coordinates on the map.

This page mainly consists of two sections: the map itself and a table that shows the data present in the database.

GeoLinker UI

⇒ User Interface

The GeoLinker map page is straightforward. There are five key areas to understand:

My Tracker For Geolinker

1. My Trackers – This shows a list of your logged devices. These names are the ones you set in the device_id section of the payload.
Clicking on All Trackers will show all devices on the map. If you are specifically looking for a particular device, click on its name.

Tracking Stats Geolinker UI

2. Tracking Stats – Displays statistics of the data sent.
You can see the Total Distance Covered (based on auto-routing shown on the map), Total Travel Time, Average Speed, and Max Speed.
All values are calculated from the data you’ve sent to the server. You can either view data for all devices or for an individual device.

Geolinker System Info

3. System Info – Shows the last updated data, overall waypoints, and provides an option to adjust the refresh rate of the webpage.

Geolinker Mean Info Icons

4. Menus – These are handy and available on the top Right. The options include Zoom In, Zoom Out, Toggle Full Screen, Measure Distance, Time Line Filter, Share GPS Data, Find My Location, Toggle Data Table, Take Screenshot, and  Map Type.

GeoLinker Current Location Message Box

5. Current Location – Shows two main features other than the actual data(Change Icon, Delete Device). The data shown includes the data count, timestamp, coordinates, battery %, speed, and sensor data.

GeoLinker Waypoint Message Box

6. Data Points – Clicking a point shows all data sent at that time, with an option to delete that specific waypoint. 

Testing the GeoLinker using Python

To quickly test the API, all you need is:

- An API key

- A few coordinates

- Timestamps

NOTE: A maximum of 10,000 data points are stored. Older points are automatically Replaced with the new ones, when this limit is reached. Works Like First In First Out.

Refer to the following code to test the API:

import requests
import json
from datetime import datetime, timedelta
import time
import random
import math
# Configuration
API_KEY = "xxxxxxxxxxxx"
DEVICE_ID = "My Bike"
API_URL = "https://www.circuitdigest.cloud/geolinker"
def send_gps_data(device_id, timestamp, lat, lng, battery, speed=None):
   """
   Send GPS data to the API endpoint and display full response
  
   Args:
       device_id: Unique identifier for the GPS device
       timestamp: Current timestamp for the GPS reading
       lat: Latitude coordinate
       lng: Longitude coordinate
       battery: Battery level percentage
       speed: Speed in km/h (optional)
  
   Returns:
       API response or None if error occurs
   """
   # Prepare payload with sensor data
   payload_data = {
       "temperature": random.uniform(24.0, 32.0),  # Random temperature between 24-32°C
       "humidity": random.uniform(60, 80)          # Random humidity between 60-80%
   }
  
   if speed is not None:
       payload_data["speed"] = speed
  
   # Prepare main data structure for API
   data = {
       "device_id": device_id,
       "timestamp": [timestamp],
       "lat": [lat],
       "long": [lng],
       # "battery": [battery],     # Commented out as per original
       # "payload": [payload_data] # Commented out as per original
   }
  
   headers = {
       "Authorization": API_KEY,
       "Content-Type": "application/json"
   }
  
   try:
       # Display the data being sent
       print(f" Sending data:")
       print(f"     Request Headers: {headers}")
       print(f"     Request Body: {json.dumps(data, indent=2)}")
      
       response = requests.post(API_URL, json=data, headers=headers)
       status_icon = "✓" if response.status_code == 200 else "✗"
      
       # Display the response details
       print(f"{status_icon} {timestamp} | Lat: {lat:.6f} | Lng: {lng:.6f} | Speed: {speed or 0:2.0f} km/h | Battery: {battery}%")
       print(f"     Response Status: {response.status_code}")
       print(f"     Response Headers: {dict(response.headers)}")
      
       try:
           response_json = response.json()
           print(f"     Response Body: {json.dumps(response_json, indent=2)}")
           return response_json
       except:
           print(f"     Response Text: {response.text}")
           return response.text
          
   except Exception as e:
       print(f"✗ Error sending data: {e}")
       return None
def send_bulk_gps_data(device_id, timestamps, latitudes, longitudes, batteries, payloads):
   """
   Send all GPS data in one bulk POST request and display full response
  
   Args:
       device_id: Unique identifier for the GPS device
       timestamps: List of timestamps
       latitudes: List of latitude coordinates
       longitudes: List of longitude coordinates
       batteries: List of battery levels
       payloads: List of payload data
  
   Returns:
       API response or None if error occurs
   """
   data = {
       "device_id": device_id,
       "timestamp": timestamps,
       "lat": latitudes,
       "long": longitudes,
       "battery": batteries,
       "payload": payloads
   }
   headers = {
       "Authorization": API_KEY,
       "Content-Type": "application/json"
   }
   try:
       print(f"\n Sending bulk data with {len(timestamps)} points...")
       print(f" Data size: {len(json.dumps(data))} characters")
      
       # Display the data being sent
       print(f"\n Sending bulk data:")
       print(f"     Request Headers: {headers}")
       print(f"     Request Body (first 3 points):")
       sample_data = {
           "device_id": device_id,
           "timestamp": timestamps[:3],
           "lat": latitudes[:3],
           "long": longitudes[:3],
           "battery": batteries[:3],
           "payload": payloads[:3]
       }
       print(json.dumps(sample_data, indent=2))
       print(f"    (Showing first 3 of {len(timestamps)} points)")
      
       response = requests.post(API_URL, json=data, headers=headers)
      
       print(f"\n Bulk Upload Response:")
       print(f"    Status Code: {response.status_code}")
       print(f"    Response Headers: {dict(response.headers)}")
      
       try:
           response_json = response.json()
           print(f"    Response Body: {json.dumps(response_json, indent=2)}")
          
           if response.status_code == 200:
               print(" Bulk data sent successfully!")
           else:
               print(f" Error in bulk upload: Status {response.status_code}")
              
           return response_json
       except:
           print(f"    Response Text: {response.text}")
           if response.status_code == 200:
               print(" Bulk data sent successfully!")
           else:
               print(f" Error in bulk upload: Status {response.status_code}")
           return response.text
          
   except Exception as e:
       print(f" Exception during bulk upload: {e}")
       return None
def calculate_speed_between_points(lat1, lng1, lat2, lng2, time_diff_minutes):
   """
   Calculate speed between two GPS coordinates using Haversine formula
   """
   if time_diff_minutes <= 0:
       return 0
  
   # Earth's radius in kilometers
   R = 6371
  
   # Convert latitude and longitude from degrees to radians
   lat1_rad = math.radians(lat1)
   lat2_rad = math.radians(lat2)
   dlat = math.radians(lat2 - lat1)
   dlng = math.radians(lng2 - lng1)
  
   # Haversine formula for great-circle distance
   a = math.sin(dlat/2)**2 + math.cos(lat1_rad) * math.cos(lat2_rad) * math.sin(dlng/2)**2
   c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
   distance_km = R * c
  
   # Calculate speed and cap at 80 km/h for realistic city driving
   speed_kmh = (distance_km / time_diff_minutes) * 60
   return min(speed_kmh, 80)
def get_traffic_delay(hour, is_main_road=False):
   """Simulate traffic-based delays"""
   base_delay = 1.0
   if 8 <= hour <= 10:
       base_delay = 1.8 if is_main_road else 2.2
   elif 18 <= hour <= 20:
       base_delay = 2.0 if is_main_road else 2.5
   elif 15 <= hour <= 16:
       base_delay = 1.4 if is_main_road else 1.6
   elif 12 <= hour <= 14:
       base_delay = 1.2
   elif 22 <= hour or hour <= 6:
       base_delay = 0.7
   return base_delay * random.uniform(0.8, 1.3)
def get_journey_data():
   """
   Define the complete journey with GPS coordinates and context information
   """
   return [
       # Starting location and initial movement
       (11.010915301164033, 77.0132087643678, "Home - Journey Start", False),
       (11.010852113196895, 77.01297272998573, "Leaving home area", False),
       (11.011368147865086, 77.01284398395916, "Local road - heading to main road", False),
      
       # Connecting to main roads
       (11.012895183932189, 77.01271523793258, "Approaching main road connection", True),
       (11.013400683781928, 77.0126508649193, "Main road junction", True),
       (11.013432277493726, 77.01226462683955, "Main road - steady progress", True),
      
       # Main journey on primary roads
       (11.012768808834144, 77.00907816268172, "Major road section - good speed", True),
       (11.012831996389947, 77.00888504364185, "Continuing on main road", True),
       (11.01294784020703, 77.00871338227309, "Main road - approaching junction", True),
       (11.01306368397854, 77.00850953439766, "Junction area - slower traffic", True),
      
       # Navigating through complex areas
       (11.013253246415422, 77.0082305846734, "Navigating through junction", True),
       (11.013379621305521, 77.00788726193586, "Post-junction - picking up speed", True),
       (11.013400683781928, 77.00755466803386, "Main road - good traffic flow", True),
       (11.013116340223256, 77.0073830066651, "Approaching destination area", True),
      
       # Arriving at destination
       (11.012789871354238, 77.00712551461193, "Destination area - slower speeds", False),
       (11.012084276110311, 77.00710405694085, "Near destination - parking search", False),
       (11.011483992258128, 77.00720061646078, "Destination reached - parking", False),
      
       # Activity at destination
       (11.010567767176969, 77.007361548994, "Parked - short walk/activity", False),
       (11.010188638723273, 77.00772632940264, "Activity at destination", False),
      
       # Starting return journey
       (11.009999074313392, 77.0082949576867, "Preparing to leave destination", False),
       (11.009588351006704, 77.0090996203528, "Starting return journey", False),
       (11.009630476500412, 77.00924982405049, "Return route - local roads", False),
       (11.009641007872897, 77.00934638357042, "Return route progress", False),
       (11.009672601988104, 77.0094107565837, "Return route - slow section", False),
       (11.009672601988104, 77.0094965872681, "Traffic light/stop", False),
      
       # Return journey main sections
       (11.009806413371894, 77.01042480446029, "Return route - main road access", True),
       (11.009668649605482, 77.00984427871586, "Return via alternate route", True),
       (11.009574719727805, 77.00914254429954, "Return journey - main section", True),
       (11.009317977909296, 77.0091935795298, "Return journey continues", True),
       (11.009023663841996, 77.00925737356766, "Return - steady progress", True),
      
       # Return journey through different areas
       (11.007991322505761, 77.00951042346873, "Return route - different path", True),
       (11.007640648812478, 77.00985491127311, "Return journey - avoiding traffic", True),
       (11.007302498784261, 77.01039078119103, "Return route - side roads", False),
       (11.007026968844638, 77.01119458606792, "Return - residential area approach", False),
       (11.00680779711771, 77.01218339365457, "Return - navigating local roads", False),
      
       # Final approach to home
       (11.00703323089158, 77.01391221208027, "Return - longer route taken", False),
       (11.007678221013876, 77.01345927441155, "Return - heading towards home area", False),
       (11.008417140020423, 77.01333168636238, "Return - approaching home locality", False),
       (11.009037079096183, 77.01320409828668, "Return - familiar roads", False),
       (11.009368965151896, 77.01313392485663, "Return - almost home", False),
       (11.009857400529931, 77.01313392485663, "Return - final approach", False),
       (11.009882448476192, 77.01332530697017, "Return - entering home area", False),
       (11.010045260075, 77.01359962133293, "Return - home locality", False),
       (11.010427240780418, 77.01384203867676, "Return - very close to home", False),
       (11.010665195723483, 77.01434601157575, "Return - final street", False),
       (11.010871840649608, 77.01433325276818, "Return - parking area", False),
       (11.011084747391523, 77.01421842350005, "Return - almost parked", False),
       (11.010959508150215, 77.01355496550643, "Return - final positioning", False),
       (11.01092819833156, 77.01342737743073, "Return - parking maneuver", False),
       (11.010890626544791, 77.0132678923361, "Home - Journey Complete", False)
   ]
def send_bulk_journey_data():
   """
   Prepare all journey data and send in one bulk API call (based on second code)
   """
   print(" BULK DATA UPLOAD MODE")
   print("=" * 60)
  
   journey_data = get_journey_data()
   start_time = datetime.now().replace(hour=14, minute=30, second=0, microsecond=0)
   current_time = start_time
   battery_level = 87
   timestamps, latitudes, longitudes, batteries, payloads = [], [], [], [], []
   print(f" Preparing {len(journey_data)} GPS points for bulk upload...")
  
   for i, (lat, lng, description, is_main_road) in enumerate(journey_data):
      
       if i == 0:
           interval = 0
           speed = 0
       else:
           # Calculate realistic time intervals
           traffic_factor = get_traffic_delay(current_time.hour, is_main_road)
           base_time = random.uniform(45, 120) if not is_main_road else random.uniform(40, 90)
           interval = int(base_time * traffic_factor)
           current_time += timedelta(seconds=interval)
          
           # Calculate speed between points
           time_diff = interval / 60.0
           prev_lat, prev_lng = journey_data[i - 1][0], journey_data[i - 1][1]
           speed = calculate_speed_between_points(prev_lat, prev_lng, lat, lng, time_diff)
           # Apply traffic conditions
           if 8 <= current_time.hour <= 10 or 18 <= current_time.hour <= 20:
               speed *= random.uniform(0.3, 0.6)  # Rush hour traffic
           elif is_main_road:
               speed *= random.uniform(0.7, 1.0)
           else:
               speed *= random.uniform(0.4, 0.8)
       # Battery drain simulation
       battery_drain = random.randint(0, 2) if i % 3 == 0 else 0
       battery_level = max(10, battery_level - battery_drain)
       # Collect data for bulk send
       timestamps.append(current_time.strftime("%Y-%m-%d %H:%M:%S"))
       latitudes.append(lat)
       longitudes.append(lng)
       batteries.append(battery_level)
       payloads.append({
           "temperature": round(random.uniform(25.0, 32.0), 2),
           "humidity": round(random.uniform(60.0, 80.0), 2),
           "speed": round(speed, 1)
       })
       print(f"  {i+1:2d}. {description[:50]:<50} | Speed: {speed:4.1f} km/h | Battery: {battery_level:2d}%")
   print(f"\n Sending all {len(timestamps)} points in one request...")
  
   # Send bulk data
   response = send_bulk_gps_data(DEVICE_ID, timestamps, latitudes, longitudes, batteries, payloads)
  
   # Summary
   print("\n" + "=" * 60)
   print(" BULK UPLOAD SUMMARY:")
   print(f" Total Points: {len(timestamps)}")
   print(f" Journey Time: {start_time.strftime('%H:%M')} - {current_time.strftime('%H:%M')}")
   print(f" Final Battery: {battery_level}%")
   print("=" * 60)
def send_single_gps_data():
   """
   Send a single GPS data point manually with full response display
   """
   print(" Send Single GPS Data Point")
   print("-" * 40)
  
   try:
       # Get user input for GPS data
       lat = float(input("Enter Latitude: "))
       lng = float(input("Enter Longitude: "))
       speed = float(input("Enter Speed (km/h, or 0 if stationary): "))
       battery = int(input("Enter Battery Level (0-100): "))
      
       # Generate current timestamp
       current_time = datetime.now()
       timestamp = current_time.strftime("%Y-%m-%d %H:%M:%S")
      
       print(f"\n Sending GPS data...")
       print(f" Location: {lat}, {lng}")
       print(f" Speed: {speed} km/h")
       print(f" Battery: {battery}%")
       print(f" Time: {timestamp}")
       print("-" * 60)
      
       # Send the data and display full response
       response = send_gps_data(DEVICE_ID, timestamp, lat, lng, battery, speed)
      
       print("-" * 60)
       if response:
           print(" Single data point sent successfully!")
       else:
           print(" Failed to send data")
          
   except ValueError:
       print(" Invalid input. Please enter valid numbers.")
   except KeyboardInterrupt:
       print("\n Operation cancelled by user.")
def run_realistic_journey_simulation():
   """
   Run individual GPS point simulation with delays and full response display
   """
   journey_data = get_journey_data()
   current_time = datetime.now().replace(hour=14, minute=30, second=0, microsecond=0)
   battery_level = 87
  
   print("=" * 100)
   print(" REALISTIC GPS JOURNEY SIMULATION WITH RESPONSE DISPLAY")
   print("=" * 100)
   print(f" Total Points: {len(journey_data)}")
   print(f" Start Time: {current_time.strftime('%Y-%m-%d %H:%M:%S')}")
   print(f" Initial Battery: {battery_level}%")
   print("=" * 100)
  
   for i, (lat, lng, description, is_main_road) in enumerate(journey_data):
      
       if i == 0:
           timestamp = current_time.strftime("%Y-%m-%d %H:%M:%S")
           speed = 0
           delay_seconds = 0
       else:
           # Calculate realistic delay
           traffic_factor = get_traffic_delay(current_time.hour, is_main_road)
           base_time = random.uniform(45, 120) if not is_main_road else random.uniform(40, 90)
           delay_seconds = int(base_time * traffic_factor)
          
           current_time += timedelta(seconds=delay_seconds)
           timestamp = current_time.strftime("%Y-%m-%d %H:%M:%S")
          
           # Calculate speed
           time_diff = delay_seconds / 60.0
           prev_lat, prev_lng = journey_data[i - 1][0], journey_data[i - 1][1]
           speed = calculate_speed_between_points(prev_lat, prev_lng, lat, lng, time_diff)
          
           # Apply traffic conditions
           if 8 <= current_time.hour <= 10 or 18 <= current_time.hour <= 20:
               speed *= random.uniform(0.3, 0.6)
      
       # Battery drain
       battery_drain = random.randint(0, 2) if i % 3 == 0 else 0
       battery_level = max(10, battery_level - battery_drain)
      
       print(f"\n{i+1:2d}. {description}")
       print(f"      Delay: {delay_seconds:3d}s |  Speed: {speed:4.1f} km/h |  Battery: {battery_level:2d}%")
       print("    " + "-" * 80)
      
       # Send GPS data with full response display
       response = send_gps_data(DEVICE_ID, timestamp, lat, lng, battery_level, speed)
      
       # Short delay for demo
       time.sleep(1)
  
   print("\n" + "=" * 100)
   print("REALISTIC JOURNEY SIMULATION COMPLETED!")
   print("=" * 100)
def main():
   """
   Main program entry point with user interface
   """
   print("Enhanced GPS Tracker Simulator with Response Display")
   print("=" * 65)
   print("Options:")
   print("1. Send single GPS data manually (with response display)")
   print("2. Run realistic journey simulation (individual points with responses)")
   print("3. Send all journey data at once (bulk upload with response)")
  
   choice = input("\nEnter your choice (1, 2, or 3): ").strip()
  
   if choice == "1":
       send_single_gps_data()
   elif choice == "2":
       print("\n Starting realistic journey simulation...")
       run_realistic_journey_simulation()
   elif choice == "3":
       send_bulk_journey_data()
   else:
       print("Invalid choice. Please run the program again and select 1, 2, or 3.")
# Run the program when executed directly
if __name__ == "__main__":
   main()

This Python code can be executed locally using any IDE such as Python’s native IDLE or PyCharm. I use Google Colab for these kinds of quick tests, you can try it too.

Once you run the code, the data points will be added to the database, and you’ll be able to view them in the GeoLinker Map UI.

Geolinker_Working_Demo working demonstration

Above, you can see a GIF/video that demonstrates the map plotting of the uploaded data points using the test code.

The last added point is marked by the icon you selected (or a larger round icon), while previous coordinates are shown as smaller connected dots. Most of the UI features are self-explanatory. With this, you’ve successfully learned how to use GeoLinker.

More Code Examples

This API has been tested using the NEO-6M with NodeMCU, but can be used with any development board capable of handling GPS data and connecting to the internet. We will be adding more projects that use the GeoLinker API, complete with all the circuit connections and code for your reference.

Create and Share

We hope this will be useful for quickly testing and deploying your ideas. If you build something using the API, please share it with us, and we will mention your work on this page. Happy building!

Projects with Location Tracker

Previously we have build many interesting projects with Location Tracker. If you want to know more about those projects, links are given below.

How to build a simple GPS Tracker using ESP32 and Visualize Data on Map

How to build a simple GPS Tracker using ESP32 and Visualize Data on Map

In this tutorial, we’ll build a GPS tracker using the ESP32 microcontroller and the Neo-6M GPS module as the main hardware components, with GeoLinker as the software component.

Build A Low Power SMS Based Vehicle Tracking System with A9G GSM+GPS Module and Arduino

Build A Low Power SMS Based Vehicle Tracking System with A9G GSM+GPS Module and Arduino

Create a GPS tracker using the A9G module and Arduino to monitor real-time location data. This project guides you through interfacing the A9G GSM+GPS module and visualizing coordinates for tracking applications.

GPS module (uBlox Neo 6M) Interfacing with AVR Microcontroller Atmega16/32

GPS module (uBlox Neo 6M) Interfacing with AVR Microcontroller Atmega16/32

Learn how to interface a GPS module with ATmega16/32 AVR microcontroller. This step-by-step tutorial shows how to receive and parse GPS data to display coordinates using UART communication and an LCD.

DIY Location Tracker using GSM SIM800 and Arduino

DIY Location Tracker using GSM SIM800 and Arduino

Build a DIY location tracker using Arduino, GSM SIM800 module, and GPS. This project sends real-time location via SMS, making it ideal for vehicle tracking or personal safety applications.

 

Have any question related to this Article?

Update 1: We are making improvements to GeoLinker API and services might be unstable till 23rd June. Use this thread for any questions or feature updates

 

Update 2:  New features that are tested include, rich maps, multiple device tracking, payload for each data point, automatic tracker updates, mobile optimization 

 

Update 3: The above documentation is currently being updated. You will see the latest API call formats by Monday (June 23 - 2025). 

Add New Comment

Login to Comment Sign in with Google Log in with Facebook Sign in with GitHub