GitHub
© 2022 Tomoya Onuki.

Tra.js - Reference

Tra.js is a library for visualizing the trajectory of time-series location data in a web browser. Dynamic changes can be visualized using transition. Currently supports visualization using Canvas element and D3.js.
Visualized bus trajectories in tokyo, shibuya. I use processed location data of Shibuya hachiko bus obtained from Shibuya City Open Data. (https://www.city.shibuya.tokyo.jp/kusei/tokei/opendata/index1.html)

Getting Started

Installation

Download this
OR
npm -i https://github.com/tomoya-onuki/tra.js

Usage

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible"
          content="IE=edge">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">
    <script src="tra.min.js"></script>
    <title>Tra.js - canvas demo</title>
</head>

<body>
    <canvas width="200" height="200"></canvas>

    <script>
        let data = [];
        for (let t = 0, msec = 1000; 
             t < Math.PI / 2 * 3; 
             t += Math.PI / 120, msec += 10) {
            data.push({
                date: msec,
                x: Math.cos(t) * 90 + 100,
                y: Math.sin(t) * 90 + 100
            });
        }

        const cvs = document.querySelector('canvas');
        Trajs()
            .trajectory(data)
            .weight(5)
            .thinning(0.99)
            .length(1000)
            .damping(0.95)
            .color(3, 152, 252)
            .ticks(100)
            .label('canvas')
            .labelStyle({
                font: 'Arial',
                size: 10,
                color: '#000',
                offset: 5
            })
            .canvas(cvs)
            .animation(2, 1000, 4000, true);
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible"
          content="IE=edge">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0">
    <script src="https://d3js.org/d3.v6.js"></script>
    <script src="tra.min.js"></script>
    <title>Tra.js - d3 demo</title>
</head>

<body>
    <div id="d3-view"></div>

    <script>
        let data = [];
        for (let t = 0, msec = 1000;
             t < Math.PI / 2 * 3;
             t += Math.PI / 120, msec += 10) {
            data.push({
                date: msec,
                x: Math.cos(t) * 90 + 100,
                y: Math.sin(t) * 90 + 100
            })
        }
        const svg = d3.select("#d3-view")
            .append("svg")
            .attr("width", 200)
            .attr("height", 200);

        Trajs()
            .trajectory(data)
            .weight(5)
            .thinning(0.99)
            .length(1000)
            .damping(0.95)
            .color(3, 152, 252)
            .ticks(100)
            .label('D3.js')
            .labelStyle({
                font: 'Arial', size: 10, color: '#000', offset: 5
            })
            .roundCap(true)
            .svg(svg)
            .animation(2, 1000, 4000, true);
    </script>
</body>
</html>

import { Trajs, Trajectory } from 'trajs';

type Data = {
    date: number;
    x: number;
    y: number;
};

window.onload = () => {
    let data: Data[] = [];
    for (let t = 0, msec = 1000; 
            t < Math.PI / 2 * 3; 
            t += Math.PI / 120, msec += 10) {
        data.push({
            date: msec,
            x: Math.cos(t) * 90 + 100,
            y: Math.sin(t) * 90 + 100
        });
    }

    const cvs:HTMLCancasElement = 
    <HTMLCancasElement>document.querySelector('canvas');

    const trajs: Trajectory = Trajs()
                        .trajectory(data)
                        .weight(5)
                        .thinning(0.99)
                        .length(1000)
                        .damping(0.95)
                        .color(3, 152, 252)
                        .ticks(100)
                        .label('canvas')
                        .labelStyle({
                            font: 'Arial',
                            size: 10,
                            color: '#000',
                            offset: 5
                        })
                        .canvas(cvs)
                        .animation(2, 1000, 4000, true);
}
                

Data

Describes data structures that can be visualized with Tra.js.
Data is an array of timestamps and 2D coordinate pairs. Generally, the format of time-series position data is a string for time (e.g. YYYY-MM-DD HH:mm:ss) and latitude and longitude for coordinates.
However, Tra.js handles time in milliseconds. Tra.js does not support these conversions. For example, to convert string time to milliseconds, use Day.js.

Structure

Data must be sorted in ascending order by date.
Date(millis) lon lat
0 10 10
1000 100 50
... ... ...

Format

Data format is JSON.
[{
    date: 0, lon: 10, lat:10
}, {
    date: 1000, lon: 100, lat:50
}]

Methods

.trajectory(data: {date: number, lon: number, lat: number}[])

Load a timse-series location data.

.transform({ center : {lon : number, lat : number}, scale : number, projection : string })

Transform cordinate from (lon, lat) into (x, y). A projection is 'liner' or 'mercator'. Default is 'liner'.

.weight(w: number)

Change a trajectory weight. Default is 1.

.thinnign(t: number)

Thin a trajectory width. Default is 1.

.roundCap(r: boolean)

Change a trajectory cap style.

.color(r: number, g: number, b:number)

Change a trajectory color. Format is RGB. Range is [0, 255]. Default is rgb(0,0,0).

.opacity(o: number)

Change a trajectory opacity. Range is [0.0, 1.0]. Default is 1.0.

.damping(d: number)

Damp a trajectory oapcity. Range is [0.0, 1.0]. Default is 1.0.

.length(l: number)

Change a trajectory length. Format is milli second. Default is the difference between the maximum and minimum values of date in data.

.ticks(t: number)

Displays tikcs. Format is milli seconds. Range is t > 0. When t ≤ 0, ticks is not displayed. Default is 0.

.label(l: string)

Displays text on head of trajectory.

.labelStyle({ font: string, size: number, color: string, offset: number })

Change params for display text. Default is {font: "Arial", size: 10, color: '#000000', offset: 0 }.

.canvas(cvs: HTMLCanvasElement)

Set a canvas element.

.svg(svg: SVGElement)

Set a svg element by D3.js.

.draw(date: number)

Draw trajectory and map to given date at the date specified by the argument. Format of date is milli second.

.animation(frameRate:number, startDate: number, endDate: number, loop: boolean)

Displays an animation of the trajectory from startDate to endDate. The animation repeats when loop is true.

.start()

Start an animation of trajectory.

.pause()

Stop an animation of trajectory.

.map(topojson: JSON)

Load a map data(.topojson). Map display is available in canvas mode only.

.mapStyle({ color: string, fill: boolean, stroke: boolean })

Set params about map style. Default is {color:'#000', fill: false, stroke: true}.

Example

Canvas

Data

let data = []
for (let t = 0, msec = 1000; 
     t < Math.PI / 2 * 3;
     t += Math.PI / 120, msec += 10) {
        data.push({
            date: msec,
            x: Math.cos(t) * 90 + 100,
            y: Math.sin(t) * 90 + 100
        });
}

Static

const cvs = document.querySelector('canvas');
Trajs()
    .trajectory(data)
    .weight(5)
    .damping(0.99)
    .color(255, 255, 255)
    .canvas(cvs)
    .draw(3000);

Animation

const cvs = document.querySelector('canvas');
Trajs()
    .trajectory(data)
    .weight(5)
    .thinning(0.99)
    .length(1000)
    .damping(0.95)
    .color(3, 152, 252)
    .ticks(100)
    .canvas(cvs)
    .animation(2, 2, 1000, 4000, true);

Interaction

const cvs = document.querySelector('canvas');
const trajs = Trajs()
    .trajectory(data)
    .weight(10)
    .thinning(0.95)
    .length(1000)
    .damping(0.9)
    .color(242, 169, 51)
    .roundCap(true)
    .label('text')
    .labelStyle({
        font: 'Arial', size: 10, color: '#FFF', offset: 5
    })
    .canvas(cvs)
    .animation(2, 1000, 4000, true);

document.querySelector('#start')
    .addEventListener('click', function () {
        trajs.start();
    })
document.querySelector('#pause')
    .addEventListener('click', function () {
        trajs.pause();
    })

Animation (not use transition method.)

const cvs = document.querySelector('canvas');
const ctx = cvs.getContext('2d');

const trajs = Trajs()
    .trajectory(data)
    .weight(2)
    .color(237, 9, 59)
    .canvas(cvs)

let start = 1000;
let end = 3000;
let crntDate = start;
setInterval(function () {
    ctx.clearRect(0, 0, 200, 200);
    trajs.draw(cvs, crntDate);
    crntDate++;

    if (crntDate > end)
    crntDate = start;
}, 1);

Map

fetch("JPN.topojson")
    .then(response => response.text())
    .then(topojson => {
        const cvs = document.querySelector('#cvs');
        Trajs()
            .canvas(cvs)
            .transform({
                center: { lon: 137.7, lat: 37.5 },
                scale: 500,
                projection: 'mercator'
            })
            .map(topojson, "#fff", false, true)
            .draw(0);
})

D3.js

Static

const svg = d3.select("#d3-view")
.append("svg")
.attr("width", 200)
.attr("height", 200);

Trajs()
    .trajectory(data)
    .weight(5)
    .damping(0.99)
    .color(255, 255, 255)
    .svg(svg)
    .draw(3000);

Animation

const svg = d3.select("#d3-view")
.append("svg")
.attr("width", 200)
.attr("height", 200);

Trajs()
    .trajectory(data)
    .weight(5)
    .thinning(0.99)
    .length(1000)
    .damping(0.95)
    .color(3, 152, 252)
    .ticks(100)
    .label('D3.js')
    .labelStyle({
        font: 'Arial', size: 10, color: '#FFF', offset: 5
    })
    .roundCap(true)
    .svg(svg)
    .animation( 2, 1000, 4000, true);