В современном мире взаимосвязанных устройств активно развиваются технологии Интернета вещей (IoT). Отслеживание объектов, мониторинг перемещения и анализ маршрутов — одни из самых важных задач для мнигих компаний, но кроме того что у этих вещей "под капотом" (backend), огромную роль играет и удобный и интуитивно понятный пользовательский интерфейс.
![]()
Сложность создания полноценного GPS-трекера на базе Интернета вещей (IoT) кроется за кулисами, в бэкенд-инфраструктуре пользовательского интерфейса и её поддержке. Разработка надёжной системы сбора, хранения и визуализации GPS-данных не всегда доступна каждому отдельному человеку или организации, работающей над IoT-решениями для GPS-трекеров. Идеальным решением был бы API, управляющий всеми задачами визуализации для IoT-трекеров. Достаточно лишь отправлять координаты, а API сам сохраняет данные в базе данных и отображает координаты.
Зачем проектам GPS-трекеров Интернета вещей нужен GeoLinker
Когда речь идет о проектах GPS-трекинга на базе Интернета вещей, API CircuitDigest Cloud GeoLinker практически идеален, поскольку он предлагает бесплатное и готовое к использованию решение для GPS-трекинга, которое упрощает обработку данных о местоположении для устройств отслеживания Интернета вещей.
GeoLinker сочетает в себе эффективность бэкэнда и интуитивно понятный интерфейс, обеспечивая GPS-отслеживание в режиме реального времени и визуальную аналитику через простой в использовании интерфейс. Вот краткий обзор его ключевых функций:
- Отображение местоположения в реальном времени на интерактивных картах с автоматическим отслеживанием маршрута;
- Аналитика в режиме реального времени, показывающая пройденное расстояние, среднюю скорость и время в пути;
- Автоматически обновляемая панель управления для непрерывного мониторинга;
- Поддержка нескольких устройств для одновременного отслеживания нескольких GPS-устройств;
- Ведение истории данных с метками времени и точными координатами;
- Параметры экспорта для отчетов или дальнейшего анализа;
- Двойной вид пользовательского интерфейса: карта + табличные данные для гибкого исследования;
- Профессиональная панель инструментов с сортируемыми столбцами, фильтрами и инструментами карт.
GeoLinker обеспечивает бесперебойный сбор географических координат, их эффективное хранение и представление с помощью многофункциональной, удобной для пользователя платформы. Это решение стирает разрыв между техническими возможностями и простотой использования, делая профессиональный GPS-отслеживание доступным для каждого.
Что такое CircuitDigest Cloud?

CircuitDigest Cloud — это инициатива, направленная на предоставление инженерам, производителям и любителям необходимых инструментов для быстрого прототипирования. Помимо API GeoLinker, данный сервис также предлагает другие полезные API, такие как сканирование QR-кодов , распознавание номерных знаков и SMS-API, для тех, кто заинтересован в расширении своих проектов.
Чтобы использовать GeoLinker для вашей системы GPS-слежения на базе Интернета вещей, вам понадобится API-ключ для аутентификации, который можно получить при регистрации учётной записи на CircuitDigest.Cloud. Но для начала нужно создать учётную запись на этом сервисе.
Регистрация новой учетной записи в CircuitDigest Cloud

Шаг 1: Посетите домашнюю страницу Circuit Digest Cloud. Нажмите кнопку "Login" («Войти») в правом верхнем углу, чтобы перейти на страницу входа.
Шаг 2: Если у вас уже есть учётная запись, войдите в систему, используя свои текущие данные. Если нет, перейдите на страницу регистрации, чтобы создать учётную запись, заполнив необходимые данные. Для этого нажмите "Register Now" («Зарегистрироваться»).
Шаг 3: После регистрации используйте свой адрес электронной почты и пароль для входа на страницу входа.
Следующий шаг — генерация ключа API.
Генерация ключа API

Шаг 4: После входа в систему нажмите "My Account" («Моя учетная запись») в правом верхнем углу.
Шаг 5: Вы будете перенаправлены на страницу, где сможете сгенерировать ключ API. Введите текст капчи в соответствующее поле и нажмите кнопку "Generate API Key" («Сгенерировать ключ API)» .
Шаг 6: Если капча верна, вы увидите таблицу с вашим API-ключом, сроком его действия и количеством использований. Нажмите на значок копирования рядом с API-ключом, и ваш ключ мгновенно скопируется в буфер обмена.
В настоящее время существует ограничение на количество использований одного ключа для SMS и API ANPR, а также на 10 000 точек данных для GPS-отслеживания. По достижении этого лимита вы можете сгенерировать другой ключ, который обеспечит ещё 100 использований. Это ограничение использования введено для предотвращения перегрузки сервера.
Конфигурация конечной точки API
| Параметр | Значение |
| Базовый URL-адрес | https://www.circuitdigest.cloud/geolinker |
| HTTP-метод | POST |
| Тип контента | application/json |
Рассмотрим приведенные в таблице параметры более подробно
Заголовки
Авторизация
- Тип: строка
- Требуется: Да
- Описание: API-ключ для безопасного доступа (например, «rB007iKt0soY»).
Тип контента
- Тип: строка
- Требуется: Да
- Описание: Должно быть «application/json»
Структура тела запроса
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{ "device_id": "string", "timestamp": ["timestamp1", "timestamp2", ...], "lat": [lat1, lat2, ...], "long": [long1, long2, ...], "battery": [battery_level] (optional), "payload": [ { "custom_key1": value1, "custom_key2": value2 } ] (optional) } |
Обязательные поля
- device_id: строковый идентификатор устройства, отправляющего данные
- timestamp: Массив строк временных меток в формате «ГГГГ-ММ-ДД ЧЧ:ММ:СС»
- lat: Массив координат широты (плавающие значения)
- long: Массив координат долготы (плавающие значения)
Необязательные поля
- battery: массив, содержащий процент уровня заряда батареи (0-100)
- payload: массив пользовательских пар «ключ-значение» для дополнительных данных датчиков
Типы данных и проверка
| Поле | Тип | Формат | Диапазон | Описание |
| timestamp (метка времени) | массив строк | "YYYY-MM-DD HH:MM:SS" («ГГГГ-ММ-ДД ЧЧ:ММ:СС»)
(например, «2025-01-09 12:30:45») |
- | временные метки UTC с настраиваемым смещением часового пояса |
| lat (Latitude - широта) | массив типа float | десятичные градусы (например, 12,971598) | от -90,0 до +90,0 | координаты широты, преобразованные из формата NMEA |
| long (Longitude - долгота) | массив типа float | десятичные градусы (например, 77,594566) | от -180,0 до +180,0 | координаты долготы, преобразованные из формата NMEA |
| battery (батарея, опционально) | массив типа integer | - | 0-100 | процент уровня заряда батареи |
| payload (полезная нагрузка, опционально) | массив объектов | - | - | данные пользовательских датчиков в виде пар «ключ-значение» |
Пример запроса
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "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 } ] } |
Коды ответов:
Успешные ответы
| Код | Статус | Описание |
| 200 | OK | Данные успешно получены и обработаны |
| 201 | Created | Создана новая запись устройства и вставлены данные |
Ответы об ошибках
| Код | Статус | Описание |
| 400 | Bad Request | Неверный формат полезной нагрузки или отсутствуют обязательные поля |
| 401 | Unauthorized | Неверный или отсутствующий ключ API |
| 500 | Internal Server Error | Ошибка обработки на стороне сервера |
Правила проверки
- Обязательные поля: все основные поля (device_id, timestamp, lat, long) должны присутствовать.
- Согласованность длины массива: массивы меток времени, широты и долготы должны иметь одинаковую длину.
- Границы координат: широта должна быть в пределах от -90 до +90, долгота — от -180 до +180.
- Ключ API: необходимо предоставить действительный заголовок авторизации.
- Формат данных: Координаты не должны быть 0.0, 0.0 (это считается недействительным GPS-определением).
Настройка GeoLinker для устройств отслеживания Интернета вещей
Вот как настроить GeoLinker для вашего проекта IoT GPS-трекера.

⇒ Загрузка данных
Используя API, упомянутый выше, вы можете отправлять данные с вашего IoT-трекера в GeoLinker. Данные будут нанесены на карту, маршрутизированы и снабжены временными метками, а также дополнены множеством дополнительных функций.
![]()
⇒ Визуализация данных
Для визуализации просто войдите в свой идентификатор CircuitDigest Cloud и откройте пользовательский интерфейс GeoLinker с помощью кнопки Track («Отслеживать»), расположенной на карточке GeoLinker на главной странице.
Сначала вы можете увидеть сообщение "No GPS Found" («GPS не найден») — это означает, что вам необходимо обновить страницу. Это необходимо для появления первой точки данных. После этого страница обновится автоматически.
После этого вы сможете увидеть нанесенные на карту координаты.
Эта страница в основном состоит из двух разделов: самой карты и таблицы , в которой отображаются данные, имеющиеся в базе данных.

⇒ Пользовательский интерфейс
Страница карты GeoLinker проста в использовании. Необходимо понимать пять ключевых моментов:
![]()
1. Мои трекеры — здесь отображается список зарегистрированных устройств. Эти имена указаны в разделе device_id полезной нагрузки.
Нажатие на All Trackers («Все трекеры») отобразит все устройства на карте. Если вы ищете конкретное устройство, нажмите на его название.
![]()
2. Статистика отслеживания – Отображает статистику отправленных данных.
Вы можете увидеть общее пройденное расстояние (на основе автоматической прокладки маршрута, отображаемой на карте), общее время в пути, среднюю и максимальную скорость.
Все значения рассчитываются на основе данных, отправленных вами на сервер. Вы можете просматривать данные как для всех устройств, так и для отдельного.

3. Информация о системе — показывает последние обновленные данные, общие путевые точки и предоставляет возможность настроить частоту обновления веб-страницы.
![]()
4. Меню — удобное и доступно в правом верхнем углу. Доступны следующие пункты: Zoom In («Увеличение масштаба»), Zoom Out («Уменьшение масштаба»), Toggle Full Screen («Переключить на весь экран»), Measure Distance («Измерение расстояния»), Time Line Filter («Фильтр по временной шкале»), Share GPS Data («Поделиться данными GPS»), Find My Location («Найти моё местоположение»), Toggle Data Table («Переключить таблицу данных»), Take Screenshot («Сделать скриншот») и Map Type («Тип карты»).

5. Текущее местоположение – отображает две основные функции, помимо самих данных (значок «Изменить», «Удалить устройство»). Отображаемые данные включают количество данных, временную метку, координаты, процент заряда батареи, скорость и данные датчиков.

6. Точки данных. При нажатии на точку отображаются все данные, отправленные в тот момент, с возможностью удаления конкретной точки маршрута.
Тестирование GeoLinker с использованием Python
Чтобы быстро протестировать API для вашей системы GPS-слежения на базе Интернета вещей, вам нужно всего лишь:
- API-ключ
- Несколько координат
- Временные метки
ПРИМЕЧАНИЕ: Сохраняется не более 10 000 точек данных. Старые точки автоматически заменяются новыми по принципу FIFO (первым пришёл — первым ушёл) при достижении этого лимита.
Для проверки API воспользуйтесь следующим кодом:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
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() |
Этот код Python можно выполнить локально с помощью любой IDE , например, родной для Python IDLE или PyCharm . Для таких быстрых тестов я использую Google Colab , можете попробовать и вы.
После запуска кода точки данных будут добавлены в базу данных, и вы сможете просмотреть их в пользовательском интерфейсе карты GeoLinker.

На представленном рисунке вы можете увидеть GIF-файл/видео, демонстрирующее построение карты загруженных точек данных с использованием тестового кода.
Последняя добавленная точка отмечена выбранным вами значком (или более крупным круглым значком), а предыдущие координаты отображаются в виде небольших соединенных точек. Большинство функций пользовательского интерфейса интуитивно понятны. Итак, вы успешно освоили GeoLinker.
Репозиторий GitHub
Все материалы для данного проекта вы можете скачать по следующей ссылке.
Примеры проектов
Этот API был протестирован на NEO-6M с NodeMCU, но может использоваться с любой платой разработки, способной обрабатывать данные GPS и подключаться к интернету. Хотя у нас уже есть несколько проектов IoT GPS-трекеров, использующих API GeoLinker, мы будем добавлять новые.
Существующие проекты:
15 просмотров



