EN

Mario Rocafull / Albin

Programador freelance en Valencia

4 octubre 2023 · Código

Cómo utilizar Traccar API

Traccar es una empresa de geolocalización de vehículos en tiempo real. Os dejo algunos ejemplos funcionales que os ahorrarán frustraciones.

Aunque su API es relativamente sencilla, ha sido un poco tedioso conseguir que funcione debido a algunos detalles omitidos en la documentación, a lo escuetas que son sus respuestas en el foro y a lo confusos que son los mensajes de error (lo que parece un problema de 'collations' es realmente un problema de autentificación). Así que he decidido publicar lo aprendido por si ayuda a alguien más.

En este artículo encontrarás dos formas de obtener las posiciones desde PHP y una forma común de mostrarlas en un mapa con Javascript.

Obtener las posiciones desde PHP (API v5)

En esta versión primero debes generar un token desde la propia plataforma y después ya puedes utilizarlo para identificarte en las subsiguientes llamadas.

Te muestro una imagen de dónde conseguir el token.

Y aquí te dejo un sencillo ejemplo de código.

<?

$path   = __DIR__.'/';
$action = $_GET['action'] ?? null;

if($action=='positions') {
	if($curl = curl_init('http://«Your Traccar Server»/api/positions')) {
		$headers = [];
		$headers[] = 'Authorization: Bearer «Your API Token»';

		curl_setopt($curl, CURLOPT_COOKIEFILE,     .'traccar.cookies');
		curl_setopt($curl, CURLOPT_COOKIEJAR,      .'traccar.cookies');
		curl_setopt($curl, CURLOPT_USERAGENT,      'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0');
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST,  'GET');
		curl_setopt($curl, CURLOPT_HTTPHEADER,     $headers);
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLINFO_HEADER_OUT,    1);
		$resp = curl_exec($curl);
		$info = curl_getinfo($curl);
		curl_close($curl);
		if($resp!==false && is_string($resp)) {
			header('Content-Type: application/json');
			die($resp);
		} else {
			header('Content-Type: text/plain');
			var_dump($resp, $info);
			die();
		}
	}
}
?>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="initial-scale=1,width=device-width" />
	<title>Traccar API Demo</title>
	<style>
	body {
		margin : 20px;
		background: #F5F5F5;
	}
	.gmaps {
		aspect-ratio: 16 / 8;
	}
	</style>
</head>
<body>

	<div id="gmaps" class="gmaps"></div>

	<script src="traccar.js"></script>
</body>
</html>

Crear una sesión y obtener las posiciones desde PHP (API v4)

El primer paso para utilizar la API es crear una sesión. Con esto obtendrás una Cookie y un Token que solo sirve para confundir.

El segundo paso ya es obtener las posiciones de los diferentes dispositivos registrados.

Algunos detalles a tener en cuenta.

  • Aunque el parámetro que enviamos se llama 'email' en verdad es el nombre de usuario, por ejemplo 'admin'.
  • En la configuración de CURL es importante mantener tanto 'CURLOPT_COOKIEFILE' como 'CURLOPT_COOKIEJAR'.
  • He dejado en el código el cómo recuperar el token aunque no tiene utilidad posterior.
<?

$path   = __DIR__.'/';
$action = $_GET['action'] ?? null;

if(empty($action)) {
	if($curl = curl_init('https://«Your Traccar Server»/api/session')) {
		$headers = [];
		$headers[] = 'Content-Type: application/x-www-form-urlencoded';

		curl_setopt($curl, CURLOPT_COOKIEFILE,     .'traccar.cookies');
		curl_setopt($curl, CURLOPT_COOKIEJAR,      .'traccar.cookies');
		curl_setopt($curl, CURLOPT_USERAGENT,      'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0');
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST,  'POST');
		curl_setopt($curl, CURLOPT_POST,           true);
		curl_setopt($curl, CURLOPT_POSTFIELDS,     http_build_query(['email'=>'«Your user»','password'=>'«Your password»']));
		curl_setopt($curl, CURLOPT_HTTPHEADER,     $headers);
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLINFO_HEADER_OUT,    1);
		$resp = curl_exec($curl);
		$info = curl_getinfo($curl);
		curl_close($curl);
		if($resp!==false && is_string($resp)) {
			$resp = json_decode($resp);
			if(is_object($resp)) {
				$token = $resp->token;
			} else {
				die(json_last_error_msg());
			}
		} else {
			header('Content-Type: text/plain');
			var_dump($resp, $info);
			die();
		}
	}
}

if($action=='positions') {
	if($curl = curl_init('http://«Your Traccar Server»/api/positions')) {
		$headers = [];
		$headers[] = 'Authorization: Basic '.base64_encode('«Your user»:«Your password»');

		curl_setopt($curl, CURLOPT_COOKIEFILE,     .'traccar.cookies');
		curl_setopt($curl, CURLOPT_COOKIEJAR,      .'traccar.cookies');
		curl_setopt($curl, CURLOPT_USERAGENT,      'Mozilla/5.0 (Windows NT 6.2; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0');
		curl_setopt($curl, CURLOPT_CUSTOMREQUEST,  'GET');
		curl_setopt($curl, CURLOPT_HTTPHEADER,     $headers);
		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
		curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($curl, CURLINFO_HEADER_OUT,    1);
		$resp = curl_exec($curl);
		$info = curl_getinfo($curl);
		curl_close($curl);
		if($resp!==false && is_string($resp)) {
			header('Content-Type: application/json');
			die($resp);
		} else {
			header('Content-Type: text/plain');
			var_dump($resp, $info);
			die();
		}
	}
}
?>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="initial-scale=1,width=device-width" />
	<title>Traccar API Demo</title>
	<style>
	body {
		margin : 20px;
		background: #F5F5F5;
	}
	.gmaps {
		aspect-ratio: 16 / 8;
	}
	</style>
</head>
<body>

	<div id="gmaps" class="gmaps"></div>

	<script src="traccar.js"></script>
</body>
</html>

Visualización de los vehículos en el mapa

Este código para mostrar los vehículos cumple las siguientes directrices:

  • Si aparece un vehículo nuevo, se crea un nuevo marker
  • Si desaparece un vehículo, se esconde su marker
  • Consulta la ubicación de los vehículos cada 1 segundo.
  • Mantiene todos los vehículos visibles ajustando el zoom del mapa hasta que el usuario interacciona con el zoom, después se respeta la decisión del usuario.
oTracer = {
	markers: {},
	autozoom: true,
	init: function (gmap) {
		this.gmap = gmap;
		this.icon = new google.maps.MarkerImage('/flag0.svg', new google.maps.Size(32, 48), new google.maps.Point(0,0), new google.maps.Point(16, 48));
		this.hint = window.setInterval(this.update.bind(this), 1000);
		this.update();
		google.maps.event.addListener(this.gmap, 'zoom_changed', (e) => {
			this.autozoom = false;
		});
	},
	update: function () {
		fetch('traccar.php?action=positions').then(resp => {
			resp.json().then(info => {
			//	console.log(info);
				info = info.filter(location => location.latitude!==0 && location.longitude!==0);
				const deviceIds = [];
				const bounds = new google.maps.LatLngBounds();
				info.map(location => {
					deviceIds.push(location.deviceId);
					let   marker = this.markers[location.deviceId] ?? null;
					const latlng = new google.maps.LatLng(location.latitude, location.longitude);
					if(marker) {
						marker.setPosition(latlng);
					} else {
						marker = new google.maps.Marker({ map: this.gmap, position: latlng, icon: this.icon,  title: 'Vehículo '+location.deviceId });
						this.markers[location.deviceId] = marker;
					}
					bounds.extend(latlng);
				});
				for(let deviceId in this.markers) {
					deviceId = parseInt(deviceId);
					this.markers[deviceId].setVisible(deviceIds.includes(deviceId));
				}
				if(this.autozoom) {
					this.gmap.fitBounds(bounds); //auto-zoom
					this.gmap.panToBounds(bounds); //auto-center
				}
			});
		});
	},
};

oGMaps = {
	init: function () {
		var elm    = document.querySelector('.gmaps');
		var script = document.createElement('script');
		script.src = '//maps.google.com/maps/api/js?key=«Your Google Maps API Key goes here»';
		script.onload = () => {
			const latlng  = new google.maps.LatLng(40.444, -3.69747);
			const mapopts = { center: latlng, zoom: 6, mapTypeId: google.maps.MapTypeId.ROADMAP };
			const gmaps   = new google.maps.Map(document.querySelector('.gmaps'), mapopts);
			oTracer.init(gmaps);
		}
		document.head.appendChild(script);
	},
};

window.addEventListener('DOMContentLoaded', oGMaps.init.bind(oGMaps));