Project

General

Profile

CrowdStreams v0.1.x

Révision de la version de base : https://projets-labinfo.he-arc.ch/projects/crowdstreams/repository?utf8=%E2%9C%93&rev=v0.1.0
Démonstration en ligne : http://crowdstreams.tic-dev.he-arc.ch/v0.1/

Etat et mode d'emploi de l'application

La version 0.1.0 de l'application CrowdStreams (Front-End) permet la visualisation des différentes données fournies jusqu'ici :

  • Tronçons de routes, avec ou sans données supplémentaires (vitesse moyenne au temps X)
  • Voitures avec données supplémentaires (vitesse au temps X)

Cette version permet aussi un minimum d'interactions :

  1. Chargement des données (première chose à faire avant toute chose)
  2. Choix des objets à afficher sur la carte
  3. Changement du temps sur la carte
  4. Récupération d'informations sur les objets présents sur la carte en cliquant dessus (informations affichées dans la console Javascript)

Les tronçons de routes affichés en bleu sont ceux qui ne possèdent pas de données sur le temps, on se contente d'afficher leur représentation graphique. Ceux qui en possèdent en revanche ont une autre couleur qui varie du rouge au vert selon le temps :

  • Rouge : voitures du tronçon à l'arrêt
  • Vert : voitures du tronçon à la vitesse maximale

Note : avec les données disponibles en date du 22 septembre 2015, les données des tronçons de routes ne varient que de 00h06m00s à 10h00m00s.

Il y a également la possibilité d'afficher les voitures avec leurs données sur le temps.

Note : avec les données disponibles en date du 22 septembre 2015, les données des voitures ne varient que de 00h00m10s à 00h00m30s.

Traitements pour et par l'application

L'application travaille avec les données TSV reçues :

  • Les noeuds et informations statiques des tronçons de routes sont extraits de Street_Graph
  • Les données sur le temps des tronçons de routes sont extraites de Road_Info
  • Les voitures et leurs données sur le temps sont extraites de Car_Info

Un premier pré-traitement de ces fichiers a été effectué pour en simplifier leur lecture par l'application :

  • "street_graph.txt" avec noeuds et informations statiques des tronçons de routes mélangés
    • Création de "nodes.csv" contenant uniquement les noeuds
    • Création de "edges.csv" contenant uniquement les informations statiques des tronçons de routes
  • "task3edges_10.txt" avec les données sur temps des tronçons de routes, mais plusieurs jours mélangés
    • Création de "edge-data.csv" contenant les données sur le temps des tronçons de routes pour un seul jour
  • "task3cars_1000.txt" avec les données sur le temps des voitures
    • Création de "car-data.csv" avec ces mêmes données

L'application AngularJS travaille directement avec ces fichiers CSV (qui contiennent des données TSV...).
Le chargement de ces derniers s'effectue à l'aide de requêtes AJAX ($http d'AngularJS), ce qui implique que l'application doit se trouver sur un serveur HTTP. Le cas échéant, les requêtes AJAX vont tenter d'accéder aux fichiers en utilisant le protocole FILE, ce qui est strictement interdit pour des raisons de sécurité.

L'application AngularJS "CrowdStreams" s'initialise au chargement de la page principale (index.html), de quoi s'ensuit le chargement de l'API Leaflet, la configuration et l'affichage de la carte OpenStreetMap.

Lors du chargement des entités et afin de visualiser les routes, l'application va faire une jointure entre les tronçons de routes et les noeuds afin de déterminer les deux extrémités de chaque tronçon.
Certains tronçons (edges) ne possèdent pas leurs deux noeuds (nodes), ils sont alors ignorés lors du chargement des entités car impossibles à afficher.

Une autre jointure effectuée par l'application est celle des données sur le temps des tronçons de routes sur leur tronçon respectif.

Points à discuter pour la séance du 25 septembre 2015

Résumé

  • Formater les données en JSON pour permettre des jointures imbriquées à la génération des fichiers
  • Gestion des jours pour les données sur le temps à l'aide d'un Timestamp UNIX
  • Acquérir de nouvelles données (plus concrètes et complètes, si possible en JSON)
  • Clés (node1;node2) parfois inversées entre les tronçons et leurs données respectives : est-ce normal ?
  • Le programme perd en rapidité lorsqu'il affiche toutes les lignes à disposition : doit-on optimiser ou est-ce qu'il y aura moins à afficher dans les cas réels ?

Format des données

Actuellement au format TSV et sans jointures, les données actuelles ne sont pas optimales pour une application Front-End.

Un premier problème est la réalisation de jointures côté client, car elles impliquent une plus grande utilisation des ressources clientes. Celles-ci devraient être effectuées à la génération des fichiers (côté Back-End).

On retrouve actuellement 4 données distinctes :

  1. Les noeuds
  2. Les tronçons de routes
  3. Les données sur le temps de tronçons de routes
  4. Les données sur le temps de voitures

Comme indiqué plus haut, l'application effectue une jointure entre les tronçons de routes et leurs noeuds pour pouvoir visualiser les tronçons.

nodes.csv (v0.1.0)

node_id latitude longitude
node_id latitude longitude
node_id latitude longitude
...

edges.csv (v0.1.0)

node_id1 node_id2 distance lanes_count avg_max_speed
node_id1 node_id3 distance lanes_count avg_max_speed
...

Comme il n'y a pas d'intérêts (côté client) à seulement obtenir les noeuds, il serait préférable d'effectuer une jointure de ces données à la génération des fichiers.

edges.tsv (nouvelle version TSV)

node_id1 latitude1 longitude1 node_id2 latitude2 longitude2 distance lanes_count avg_max_speed
node_id1 latitude1 longitude1 node_id2 latitude2 longitude2 distance lanes_count avg_max_speed
...

Mais maintenant survient un deuxième problème si on souhaite effectuer les jointures à la génération des fichiers : les fichiers TSV ne possèdent que des données "plates". Cela pose problème pour les données des tronçons à joindre qui possèdent plus qu'une seule ligne pour un même tronçon.

edge-data.csv (v0.1.0)

time1 node_id1 node_id2 avg_speed
...
time2 node_id1 node_id2 avg_speed
...
time3 node_id1 node_id2 avg_speed
...

Solution au problème : passer à un format de données qui gère les structures imbriquées. Comme AngularJS prône le format JSON (et peut le parser automatiquement sans l'ajout de plugin), il s'agit là du candidat idéal.

edges.json (nouvelle version JSON)

[
    {
        "node1": {
            "nodeId": Integer,
            "latitude": Decimal,
            "longitude": Decimal
        },
        "node2": {
            "nodeId": Integer,
            "latitude": Decimal,
            "longitude": Decimal
        },
        "distance": Decimal,
        "lanesCount": Integer,
        "avgMaxSpeed": Decimal,
        "dataOverTime": [
            {
                "time": Integer,
                "avgSpeed": Decimal
            },
            { ... },
              ...
            { ... }
        ]
    },
    { ... },
      ...
    { ... }
]

cars.json (nouvelle version JSON)

[
    {
        "carId": Integer,
        "dataOverTime": [
            {
                "time": Integer,
                "latitude": Decimal,
                "longitude": Decimal,
                "speed": Decimal
            },
            { ... },
              ...
            { ... }
        ]
    },
    { ... },
      ...
    { ... }
]

On pourrait reprocher cette approche d'être trop verbose par rapport à sa version TSV. Ce problème ne s'applique pas réellement puisqu'on lit des fichiers locaux (pas de problèmes de bande passante).
De plus, même si on récupérait ces données depuis un endpoint en ligne et qu'on avait des problèmes de bande passante, il y a la possibilité de passer par du JSON compressé ou du JSON où on ne passe que des "tableaux de tableaux".

Le JSON compressé est à configurer sur le serveur (gzip) et est automatiquement décompressé par le browser client en présente de l'en-tête "Content-Encoding: gzip".

edges.json (alternative "tableaux de tableaux", on perd les identifiants et on doit se fier aux positionnements, mais on garde l'imbrication)

[
    [
        [node_id1, latitude1, longitude1],
        [node_id2, latitude2, longitude2],
        distance,
        lanesCount,
        avgMaxSpeed,
        [
            [time1, avgSpeed1],
            [time2, avgSpeed2],
            ...
            [timeN, avgSpeedN]
        ]
    ],
    [ ... ],
      ...
    [ ... ]
]

cars.json (alternative "tableaux de tableaux", on perd les identifiants et on doit se fier aux positionnements, mais on garde l'imbrication)

[
    [
        car_id,
        [
            [time1, latitude1, longitude1, speed1],
            [time2, latitude2, longitude2, speed2],
            ...
            [timeN, latitudeN, longitudeN, speedN]
        ]
    ],
    [ ... ],
      ...
    [ ... ]
]

Gestion des jours pour les données sur le temps

Actuellement, les données sur le temps que l'on possède ne gère qu'un temps en secondes par rapport à un jour (par pas de 6 minutes sur 10 heures).

...
"dataOverTime": [
    {
        "time": 360,
        "avgSpeed": 40.2
    },
    {
        "time": 720,
        "avgSpeed": 35.6
    },
...

Comme on souhaite pouvoir gérer la date en plus du temps par rapport à un jour, il y a un ajout d'informations à faire pour obtenir la date. La solution la plus courante pour marquer un temps donné est le Timestamp (temps écoulé depuis le 1er janvier 1970 à 00h00m00s UTC). Dans notre cas, un Timestamp UNIX (précision à la seconde) est exactement ce qu'il faut.

...
"dataOverTime": [
    {
        "timestamp": 1442873160,
        "avgSpeed": 40.2
    },
    {
        "timestamp": 1442873520,
        "avgSpeed": 35.6
    }
...

Acquisition de nouvelles données

Certaines données sont manquantes : certains tronçons de routes ne possèdent par tous leurs noeuds et on ne peut afficher que la vitesse routière et les voitures sur le temps.
L'idéal serait d'acquérir de nouvelles données (si possible en format JSON comme décrit au chapitre précédent) plus concrètes que les extraits actuels :

  • Nouvelles données pour d'autres visualisation : densité routière, taux d'évolution du trafic, taux de congestion, etc. (comme définit lors de la dernière séance PV_22052015)
  • Données complètes : pas de noeuds orphelins / tronçons incomplets

Tronçons de routes et données sur le temps

Parfois, les clés des tronçons de routes étaient inversées par rapport aux clés des données sur le temps des tronçons de routes correspondant.

edges.csv (v0.1.0)

node_id1 node_id2 distance lanes_count avg_max_speed
...

edge-data.csv (v0.1.0)

time1 node_id2 node_id1 avg_speed
...
time2 node_id2 node_id1 avg_speed
...
time3 node_id2 node_id1 avg_speed
...

S'agit-il d'une erreur ou est-ce volontaire (un tableau de données sur le temps pour un sens de la route et un autre pour l'autre sens) ?

Nombre d'objets à afficher par l'application

Avec les données actuelles, on peut afficher la quasi-totalité du réseau routier de Warsaw. Si on charge l'intégralité des lignes (première checkbox dans le choix des objets à afficher de l'application) et qu'on possède un niveau de zoom qui affiche toutes les lignes, l'application perd des FPS pour la plupart des actions que l'on peut effectuer sur la carte (déplacer, zoomer, changer le temps).
Si on zoome pour réduire le nombre de ligne à l'écran, changer le temps devient plus fluide mais les déplacements/zooms restent lent.

Au final, une question se pose : est-ce que l'application finale sera amenée à afficher autant de lignes dans un cas concret (ce qui nécessiterait d'effectuer des optimisations d'affichage) ou est-ce que le nombre d'objets affichés sera limités, au point qu'une optimisation serait inutile ?

Remarques

  • Les données sur le temps sont triées par le champ "time" (futur "timestamp" ?) dans l'ordre croissant : c'est parfait, conserver ce comportement (simplifie le code client, et permettra de réduire une complexité O(n) en O(log(n))).
  • Est-ce que chaque tronçon de route aura toujours les mêmes données sur le temps que les autres tronçons de routes (même quantité avec les même tranches de temps) ? Si oui, on aura la possibilité de passer l'algorithme qui met à jour les données sur la carte selon le temps d'une complexité O(a * n) à O(a + n).