diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..7743b9b Binary files /dev/null and b/favicon.png differ diff --git a/global.R b/global.R index 55d228d..9dffb36 100644 --- a/global.R +++ b/global.R @@ -1,14 +1,27 @@ -library(shiny) -library(readr) -library(dplyr) -library(tidyr) -library(ggplot2) -library(lubridate) -library(leaflet) # For now : devtools::install_github("RCura/leaflet") -library(ggthemes) -library(ggmap) +suppressPackageStartupMessages({ + library(shiny) + library(readr) + library(dplyr) + library(tidyr) + library(ggplot2) + library(lubridate) + library(leaflet) # For now : devtools::install_github("RCura/leaflet") + library(ggthemes) + library(ggmap) + library(stringi) + library(shinyjs) + library(V8) +}) + +#enableBookmarking(store = "server") + +jsCode <- " + shinyjs.launchIntro = function(){startIntro();}; + shinyjs.launchUserIntro = function(){userDataIntro();} +" options(shiny.maxRequestSize = 8*1024^2) +source("src/helpers.R") moisFr <- c( "janv.", @@ -45,7 +58,8 @@ formatData <- function(rawData, tz){ mutate(annee = year(Time)) %>% mutate(heure = hour(Time)) %>% mutate(minute = minute(Time)) %>% - mutate(dhour = hour(Time) + minute(Time) / 60 + second(Time) / 3600) + mutate(dhour = hour(Time) + minute(Time) / 60 + second(Time) / 3600) %>% + mutate(monthWeek = stri_datetime_fields(Time)$WeekOfMonth ) if (nrow(formattedData) > 50E3){ formattedData <- formattedData %>% sample_n(size = 50E3) diff --git a/server.R b/server.R index cdfe6aa..38c336f 100644 --- a/server.R +++ b/server.R @@ -1,23 +1,94 @@ library(shiny) shinyServer(function(session, input, output) { - locationData <- - reactiveValues( + locationData <- reactiveValues( base = formattedData, geofiltred = NA, timefiltred = NA ) + analysisData <- reactiveValues(homePoint = NA, workPoint = NA) - observe({ + + + showModal(modalDialog(size = "l", + title = "Bienvenue dans TimeLine Exploratory Dashboard", + HTML("Cette application web permet à ses utilisateurs d'explorer dynamiquement + les traces GPS collectés par la société Google dans le cadre de son programme « Timeline ». + Lorsqu'un individu possède un smartphone fonctionnant avec le système « Android », + celui-ci lui propose d'enregistrer régulièrement et automatiquement les coordonnées de l'endroit où il se trouve. + Ce choix effectué, les coordonnées ainsi que l'heure seront enregistrées, + environ toutes les 5 minutes, et communiquées aux serveurs de Google. + L'utilisateur peut alors les consulter sur un site dédié : + Google Timeline.
+ Ce site ne permet qu'une consultation jour par jour, + et les données y sont en grande partie masquées, + seuls les lieux identifiés par Google y apparaissant. + On peut télécharger ces données, massives, mais les outils pour les consulter et explorer manquent. +
+ TimeLineEDB se propose de combler ce manque. +
+ Lors d'une première visite, nous vous invitons à suivre le tutoriel afin de comprendre + l'utilisation de TimeLine EDB. +
+ Notez que vous pouvez toujours revenir au tutoriel en cliquant sur l'icone aide + () + en haut à droite de l'application."), + easyClose = FALSE, + footer = tagList( + column(6, actionButton(inputId = "showHelp", label = "Suivre le tutoriel", icon = icon("education", lib = "glyphicon"))), + column(6, modalButton(label = "Entrer directement dans l'application", icon = icon("remove", lib = "glyphicon"))) + ) + )) + + + observeEvent(input$showHelp,{ + removeModal() + js$launchIntro() + }) + + observeEvent(input$mainHelp, { + js$launchIntro() + }) + + observeEvent(input$userDataHelp,{ + js$launchUserIntro() + }) + + + observeEvent(input$loadUserData,{ req(input$userData) - locationData$base <- google_jsonZip_to_DF(input$userData$datapath, input$timezone) - locationData$geofiltred <- NA - locationData$timefiltred <- NA + withBusyIndicatorServer("loadUserData", { + thisMapProxy <- leafletProxy("map") + thisMapProxy %>% + clearHeatmap() %>% + removeDrawToolbar() + showNotification(ui = "Conversion des données...", duration = NULL, closeButton = TRUE, id = "notifData", type = "message") + locationData$base <- google_jsonZip_to_DF(input$userData$datapath, input$timezone) + removeNotification( id = "notifData") + locationData$geofiltred <- NA + locationData$timefiltred <- NA + showNotification(ui = "Mise à jour de la carte", duration = NULL, closeButton = TRUE, id = "notifMap", type = "message") + thisMapProxy %>% + addDrawToolbar( + layerID = "selectbox", + polyline = FALSE, + circle = FALSE, + marker = FALSE, + edit = FALSE, + polygon = FALSE, + rectangle = TRUE, + remove = TRUE, + singleLayer = TRUE + ) + removeNotification( id = "notifMap") + + }) + }) output$map <- renderLeaflet({ - mapData <- locationData$base + mapData <- isolate(locationData$base) dataLength <- nrow(mapData) map <- leaflet(mapData) %>% addProviderTiles('CartoDB.DarkMatter', @@ -158,6 +229,14 @@ shinyServer(function(session, input, output) { noselection <- TRUE currentlyFiltred <- locationData$base + if (!is.null(input$yearplot_brush)) { + #yearfreq + timeSelection <- input$yearplot_brush + currentlyFiltred <- currentlyFiltred %>% + filter(annee >= timeSelection$xmin, annee <= timeSelection$xmax) + noselection <- FALSE + } + if (!is.null(input$daydensity_brush)) { #daydensity timeSelection <- input$daydensity_brush @@ -279,19 +358,30 @@ shinyServer(function(session, input, output) { ) }) - # # observe({ - # # if (input$map_selectbox_deleting){ - # # print("deleting...") - # # } - # # }) - # - # observeEvent(input$map_selectbox_deleting,{ - # print("blob") - # thisMapProxy <- leafletProxy("map") - # thisMapProxy %>% - # fitBounds(0,0,0,0) - # - # }) + # Change the default behavior of deleting : + # the selectbox is deleted each time the user + # clicks on the garbage/delete-mode icon + observe({ + currentDeleting <- input$map_selectbox_deleting + if (isTRUE(currentDeleting)){ + thisMapProxy <- leafletProxy("map") + thisMapProxy %>% + removeDrawToolbar() %>% + addDrawToolbar( + layerID = "selectbox", + polyline = FALSE, + circle = FALSE, + marker = FALSE, + edit = FALSE, + polygon = FALSE, + rectangle = TRUE, + remove = TRUE, + singleLayer = TRUE + ) + } else if (is.null(currentDeleting)){ + locationData$geofiltred <- NA + } + }) observeEvent(input$analysisWork, { @@ -307,6 +397,7 @@ shinyServer(function(session, input, output) { }) output$homeAddress <- renderText({ + req(input$revGeoCode) homeData <- locationData$base %>% filter(heure > 19 | heure < 8) %>% filter(moisN != "juil.", mois != "août") %>% @@ -329,6 +420,7 @@ shinyServer(function(session, input, output) { }) output$workAddress <- renderText({ + req(input$revGeoCode) workData <- locationData$base %>% filter(heure >= 14, heure <= 16) %>% filter(moisN != "juil.", mois != "août") %>% @@ -383,6 +475,73 @@ shinyServer(function(session, input, output) { } }) + output$yearPlot <- renderPlot({ + yearfreqplot <- ggplot(data = locationData$base) + + geom_bar( + aes(annee, y = (..count..) / sum(..count..)), + fill = "#43a2ca", + alpha = 0.3, + colour = "#053144" + ) + + scale_y_continuous("Densité", labels = scales::percent) + + theme_timelineEDB() + + if (length(locationData$geofiltred) > 1) { + yearfreqplot <- yearfreqplot + + geom_bar( + data = locationData$geofiltred, + aes(annee, y = (..count..) / sum(..count..)), + fill = "red", + alpha = 0.3, + colour = "#67000d" + ) + } + yearfreqplot + }, bg = "transparent") + + output$calendarPlot <- renderPlot({ + req(locationData$base) + + if (length(locationData$geofiltred) > 1) { + calendarFiltredData <- locationData$geofiltred %>% + group_by(annee, moisN, monthWeek, jourN) %>% + summarise(count = n()) %>% + mutate(jourN = factor(jourN, levels=rev(levels(jourN)))) + + calendarPlot <- ggplot(locationData$base, aes(monthWeek, jourN, fill = count)) + + geom_tile(data =calendarFiltredData, colour="#333333", alpha = 0.8) + + facet_grid(annee~moisN) + + scale_fill_gradient(name="Densité", high="red",low="#333333") + + scale_x_discrete("") + + xlab("") + + ylab("") + + theme_timelineEDB() + + theme(legend.position="bottom") + + guides(fill = guide_legend(keywidth = 5, keyheight = 2)) + + + } else { + calendarBaseData <- locationData$base %>% + group_by(annee, moisN, monthWeek, jourN) %>% + summarise(count = n()) %>% + mutate(jourN = factor(jourN, levels=rev(levels(jourN)))) + + + calendarPlot <- ggplot(calendarBaseData, aes(monthWeek, jourN, fill = count)) + + geom_tile(colour="#333333", alpha = 0.8) + + facet_grid(annee~moisN) + + scale_fill_gradient(name="Densité", high="#43a2ca",low="#333333") + + scale_x_discrete("") + + xlab("") + + ylab("") + + theme_timelineEDB() + + theme(legend.position="bottom") + + guides(fill = guide_legend(keywidth = 5, keyheight = 2)) + } + + calendarPlot + + }, bg = "transparent") }) diff --git a/src/helpers.R b/src/helpers.R new file mode 100644 index 0000000..b76796e --- /dev/null +++ b/src/helpers.R @@ -0,0 +1,84 @@ +# https://github.com/daattali/advanced-shiny/tree/master/busy-indicator +# Copyright 2016 Dean Attali. Licensed under the MIT license. + +# All the code in this file needs to be copied to your Shiny app, and you need +# to call `withBusyIndicatorUI()` and `withBusyIndicatorServer()` in your app. +# You can also include the `appCSS` in your UI, as the example app shows. + +# ============================================= + +# Set up a button to have an animated loading indicator and a checkmark +# for better user experience +# Need to use with the corresponding `withBusyIndicator` server function +withBusyIndicatorUI <- function(button) { + id <- button[['attribs']][['id']] + div( + `data-for-btn` = id, + button, + span( + class = "btn-loading-container", + hidden( + img(src = "ajax-loader-bar.gif", class = "btn-loading-indicator"), + icon("check", class = "btn-done-indicator") + ) + ), + hidden( + div(class = "btn-err", + div(icon("exclamation-circle"), + tags$b("Error: "), + span(class = "btn-err-msg") + ) + ) + ) + ) +} + +# Call this function from the server with the button id that is clicked and the +# expression to run when the button is clicked +withBusyIndicatorServer <- function(buttonId, expr) { + # UX stuff: show the "busy" message, hide the other messages, disable the button + loadingEl <- sprintf("[data-for-btn=%s] .btn-loading-indicator", buttonId) + doneEl <- sprintf("[data-for-btn=%s] .btn-done-indicator", buttonId) + errEl <- sprintf("[data-for-btn=%s] .btn-err", buttonId) + disable(buttonId) + show(selector = loadingEl) + hide(selector = doneEl) + hide(selector = errEl) + on.exit({ + enable(buttonId) + hide(selector = loadingEl) + }) + + # Try to run the code when the button is clicked and show an error message if + # an error occurs or a success message if it completes + tryCatch({ + value <- expr + show(selector = doneEl) + delay(2000, hide(selector = doneEl, anim = TRUE, animType = "fade", + time = 0.5)) + value + }, error = function(err) { errorFunc(err, buttonId) }) +} + +# When an error happens after a button click, show the error +errorFunc <- function(err, buttonId) { + errEl <- sprintf("[data-for-btn=%s] .btn-err", buttonId) + errElMsg <- sprintf("[data-for-btn=%s] .btn-err-msg", buttonId) + errMessage <- gsub("^ddpcr: (.*)", "\\1", err$message) + html(html = errMessage, selector = errElMsg) + show(selector = errEl, anim = TRUE, animType = "fade") +} + +appCSS <- " +.btn-loading-container { + margin-left: 10px; + font-size: 1.2em; +} +.btn-done-indicator { + color: green; +} +.btn-err { + margin-top: 10px; + color: red; +} +" \ No newline at end of file diff --git a/ui.R b/ui.R index 9131399..4f58e9c 100644 --- a/ui.R +++ b/ui.R @@ -1,33 +1,33 @@ library(shiny) + +function(request){ + shinyUI( fluidPage( theme = "slate-bootstrap.css", + useShinyjs(), + extendShinyjs(text = jsCode), + tags$style(appCSS), tags$head( tags$link(rel = "icon", type = "image/png", href = "favicon.png"), tags$title("TimeLineEDB"), + includeScript("www/analytics.js"), includeScript("www/intro.min.js"), includeCSS("www/introjs.min.css"), includeScript("www/CustomIntro.js"), includeCSS("www/timelineEDB.css") ), - h2( - "TimeLine Exploratory DashBoard", - tags$a( - id = "mainHelp", - style = "float: right;", - icon(name = "question-circle", class = "fa-1x", lib = "font-awesome"), - href = "javascript:void(0);", - onclick = "startIntro();" - ) - ), - fluidRow(column( - 4, plotOutput("daydensity", brush = brushOpts(id = "daydensity_brush", direction = "x")) + column(11, h2("TimeLine Exploratory DashBoard")), + #column(1, bookmarkButton(label = "Sauvegarder l'état", icon = icon("save", lib = "glyphicon"))), + column(1, actionLink("mainHelp", label = "", icon(name = "question-circle", class = "fa-3x", lib = "font-awesome"))), + fluidRow( + column(4, plotOutput("daydensity", brush = brushOpts(id = "daydensity_brush", direction = "x")) ), column(8, tags$input(id = "mapSettings", type = "checkbox", class = "inv-checkbox"), - tags$label('for' = "mapSettings", icon(name = "cogs", class = "fa-1x", lib = "font-awesome")), + tags$label('for' = "mapSettings", class="mapSettingsCheckBox", icon(name = "cogs", class = "fa-1x", lib = "font-awesome")), conditionalPanel(condition = "input.mapSettings == true", checkboxInput("showClusters", label = "Afficher les clusters de points ?", value = FALSE), checkboxInput("fitToBounds", label = "Synchroniser l'étendue de la carte avec la sélection ?", value = FALSE)), @@ -44,8 +44,8 @@ shinyUI( column( 4, fluidRow( - wellPanel( - h3("Analyse automatique"), + wellPanel(id="automaticAnalysis", + h3("Analyse automatique"),checkboxInput("revGeoCode", "Lancer les analyses"), tags$hr(), "D'après analyse automatique de vos données, on peut inférer ces informations vous concernant :", tags$hr(), @@ -64,27 +64,30 @@ shinyUI( ), fluidRow( tags$input(id = "userSettings", type = "checkbox", class = "inv-checkbox"), - tags$label('for' = "userSettings", span("Explorez vos propres données", class = "btn btn-info"), + tags$label('for' = "userSettings", span("Explorez vos propres données", class = "userSettingsCheckBox btn btn-info"), onclick = "userDataIntro();"), - tags$a( - id = "userDataHelp", - icon(name = "question-circle", class = "fa-3x", lib = "font-awesome"), - href = "javascript:void(0);", - onclick = "userDataIntro();" - ) + actionLink(inputId = "userDataHelp", label = "",icon = icon(name = "question-circle", class = "fa-3x", lib = "font-awesome")) ), fluidRow( conditionalPanel(condition = "input.userSettings == true", - fluidRow(fileInput("userData", - label = "Chargez vos données", + fluidRow( + column(6, fileInput("userData", + label = "Sélectionner vos données", multiple = FALSE, accept = "application/zip", width = "100%")), - fluidRow(selectInput("timezone", label = "Fuseau horaire", + column(6,selectInput("timezone", label = "Fuseau horaire", choices = lubridate::olson_time_zones(), multiple = FALSE, selected = "Europe/Paris", - selectize = FALSE)) + selectize = FALSE))), + withBusyIndicatorUI( + actionButton("loadUserData", + label = "Charger vos données", + class="btn-info", + width="50%", + icon = icon(name = "map", lib = "font-awesome")) + ) ) ), fluidRow( @@ -99,6 +102,11 @@ shinyUI( ) ) ) + ), + fluidRow( + column(4, plotOutput("yearPlot", brush = brushOpts(id = "yearplot_brush", direction = "x"))), + column(8, plotOutput("calendarPlot")) ) ) ) +} diff --git a/www/CustomIntro.js b/www/CustomIntro.js index 07952c3..c149164 100644 --- a/www/CustomIntro.js +++ b/www/CustomIntro.js @@ -1,90 +1,76 @@ -//set the cookie when they first hit the site -function setCookie(c_name,value,exdays) -{ -var exdate=new Date(); -exdate.setDate(exdate.getDate() + exdays); -var c_value=escape(value) + ((exdays===null) ? "" : "; expires="+exdate.toUTCString()); -document.cookie=c_name + "=" + c_value; -} - -//check for the cookie when user first arrives, if cookie doesn't exist call the intro. -function getCookie(c_name) -{ -var c_value = document.cookie; -var c_start = c_value.indexOf(" " + c_name + "="); -if (c_start == -1) - { - c_start = c_value.indexOf(c_name + "="); - } -if (c_start == -1) - { - c_value = null; - } -else - { - c_start = c_value.indexOf("=", c_start) + 1; - var c_end = c_value.indexOf(";", c_start); - if (c_end == -1) - { -c_end = c_value.length; -} -c_value = unescape(c_value.substring(c_start,c_end)); -} -return c_value; -} - -function checkCookieIntro(){ - var cookie=getCookie("timelineEDB"); - - if (cookie===null || cookie==="") { - setCookie("timelineEDB", "1",90); - startIntro(); //change this to whatever function you need to call to run the intro - } -} - function startIntro(){ var intro = introJs(); intro.setOptions({ steps: [ { intro: "Bienvenue dans l'application TimeLine Exploratory DashBoard!" - }, - { - intro: "Ici, on explique l'objectif" - }, - { - intro: "Ici, on explique l'usage de l'application" - }, - { - intro : "On va maintenant présenter les composants un par un" - }, - { - element: '#map', - intro: "This is a map." - }, - { - element: '#daydensity', - intro: 'Ce graphique interactif affiche la fréquence temporelle (heures de la journée) des enregistrement de l\'utilisateur.\n
Vous pouvez effectuer une sélection sur ces heures, ce qui aura pour effet de modifier les points affichés sur la carte, et ainsi, essayer de trouver les lieux fréquentés par l\'utilisateur aux différentes heures de la journée.\n
En cas de sélection spatale, la différence entre la fréquence générale (en bleu) et la fréquence de la sélection (en rouge) vous permettront de mieux comprendre l\'usage temporel d\'un lieu, et donc sa fonction.' - }, - { - element: '#dayfreq', - intro: 'Ce graphique interactif affiche la fréquence temporelle (heures de la journée) des enregistrement de l\'utilisateur.\n
Vous pouvez effectuer une sélection sur ces heures, ce qui aura pour effet de modifier les points affichés sur la carte, et ainsi, essayer de trouver les lieux fréquentés par l\'utilisateur aux différentes heures de la journée' - }, - { - element: '#monthfreq', - intro: 'monthfreq' - }, - { - element: '.well', - intro: 'Analyse auto' - }, - { - element: '#userData', - intro: "Charger données" - }, - { - element: '#mainHelp', - intro: "Vous pouvez cliquer ici à tout moment pour ré-afficher cette présentation de TimeLine EDB.\n
Bonne exploration !" + },{ + intro: "Cette application va vous permettre d'explorer des données de localisation que Google possède sur ses utilisateurs ( et peut-être sur vous ..) au sein d'une application dénommée Google TimeLine." + },{ + intro: "L'objectif est de vous amener à comprendre des éléments de la vie privé d'un utilisateur dont on possède les données." + },{ + intro: "Ces données sont complexes et massives. Affichées toutes ensemble, elles se superposent sans donner de grandes informations sur l'individu suivi." + },{ + intro: "Mais en se \"promenant\" dans ces données, on peut aussi discerner des éléments très privés." + },{ + intro: "Pour se faire, on va faire appel aux méthodes de l'analyse exploratoire interactive de données." + },{ + intro: "Les \"composants graphiques\" (cartes, diagrammes) présentés sur cette page sont interactifs. C\'est-à-dire que vous pouvez effecter des sélections dedans, et que chacun réagit aux sélections effectuées dans les autres." + },{ + intro : "Pour faciliter la compréhension, on va maintenant présenter les composants un par un." + },{ + element: "#map", + intro: "Vous disposez ici d'une carte montrant l'ensemble des localisations de l'individu.
C'est une carte de type heatmap, ou \"carte de chaleur\".
Les points proches sont superposés, et plus il y a de points superposés, plus la couleur s'approche du bleu." + },{ + element: "#map", + intro: "Un point très bleu est donc une zone dans laquelle l'individu s'est rendu très souvent. C'est ce que l'on appelle un hot-spot, ou \"point chaud \"." + },{ + element: "#map", + intro: "Afin de comprendre à quoi correspondent ces points chauds, vous pouvez zoomer, dézoomer, mais aussi vous déplacer dans la carte." + },{ + element: ".mapSettingsCheckBox", + intro: "Des réglages pour la carte sont accessibles en cliquant sur cette icône." + },{ + element: "#map", + intro: "Vous pouvez aussi effectuer une sélection, grâce à l'outil de dessin suivant." + },{ + element: ".leaflet-draw-toolbar-top", + intro: "En dessinant une zone rectangulaire avec cet outil, une sélection spatiale des points situés à l'intérieur de la zone sera effectuée.

Cette sélection sera reprise sur l'ensemble des autres graphiques, ce qui vous permettra par exemple de comprendre à quel moment de la journée un utilisateur se rend dans un lieu donné." + },{ + element: "#daydensity", + intro: "Ce graphique interactif affiche la fréquence temporelle (heures de la journée) des enregistrement de l\'utilisateur.\n
Vous pouvez effectuer une sélection sur ces heures, ce qui aura pour effet de modifier les points affichés sur la carte, et ainsi, essayer de trouver les lieux fréquentés par l\'utilisateur aux différentes heures de la journée.\n
En cas de sélection spatale, la différence entre la fréquence générale (en bleu) et la fréquence de la sélection (en rouge) vous permettront de mieux comprendre l\'usage temporel d\'un lieu, et donc sa fonction." + },{ + element: "#daydensity", + intro: "Vous pouvez par exemple sélectionner les heures correspondant à la nuit pour trouver les endroits où l'utilisateur passe ses nuits, et donc, sans doute, son domicile." + },{ + element: "#dayfreq", + intro: "Ce graphique interactif affiche la fréquence hebdomadaire (jours de la semaine) des enregistrement de l\'utilisateur.\n
Vous pouvez effectuer une sélection sur ces jours, ce qui aura pour effet de modifier les points affichés sur la carte, et ainsi, essayer de trouver les lieux fréquentés par l\'utilisateur selon les jours de la semaine." + },{ + element: "#dayfreq", + intro: "Vous pouvez par exemple observer la différence entre la localisation de l'utilisateur en semaine et en week-end pour faire ressortir la différence de fréquentation des lieux de travail et des lieux de loisir." + },{ + element: "#monthfreq", + intro: "Comme le graphique précédent, mais celui-ci informe sur la fréquence mensuelle. Vous pouvez ainsi différencier les mois d'été, qui peuvent montrer les vacances de l'utilisateur, des mois usuels." + },{ + element: "#yearPlot", + intro: "Vous pouvez utiliser ce graphique annuel afin de différencier les localisations de l'utilisateur, par exemple pour observer si celui-ci a déménagé ou changé de lieu de travail." + },{ + element: "#calendarPlot", + intro: "Ce graphique présente une vue calendaire. Chaque carré représente un jour du mois. Plus la densité est forte (bleu clair ou rouge clair selon qu'il y a une sélection spatiale ou non), plus l'utilisateur a de points enregistrés ce jour là." + },{ + element: "#calendarPlot", + intro: "Cela permet par exemple de comprendre quand l'utilisateur a effectué un voyage particulier, ou encore d'avoir une idée de l'occupation sur l'année d'un lieu secondaire pour l'utilisateur." + },{ + element: "#automaticAnalysis", + intro: "Dans ce paneau, vous pouvez executer (en cochant l'icône \"Lancer les analyses\") une analyse automatique qui vise à deviner où l'utilisateur est domicilié et où il travaille.

Pour cela, on effectue une opération de classification des points, en observant les lieux où l'utilisateur se trouve le plus souvent. On isole ensuite parmi ces lieux l'endroit où l'utilisateur est le plus souvent pendant la journée de travail, en jours de semaines, pour trouver le lieu de travail. En effectuant la même opération pour les heures de nuit, on trouve le domicile probable.
Grâce à une opération nommée \"géocodage inverse\", on peut alors déduirGérard Augraffee l'adresse de ces deux \"hot-spots\"." + },{ + intro: "C'est la fin de cette visite guidée, vous êtes maintenant invités à explorer le jeu de données qui vous est ici présenté, en essayant de comprendre le déroulement des journées et semaines de l'utilisateur qui vous est ainsi livré." + },{ + element: ".userSettingsCheckBox", + intro: "Vous pourrez ensuite exploiter ces méthodes d'explorations sur vos propres données, en cliquant sur ce bouton." + },{ + element: "#mainHelp", + intro: "Vous pouvez cliquer ici à tout moment pour ré-afficher cette présentation de TimeLine EDB.
Bonne exploration !" } ], tooltipPosition: 'auto', @@ -102,23 +88,22 @@ function userDataIntro(){ steps: [ { intro: "Vous pouvez maintenant explorer vos propres données, si toutefois Google en possède sur vous !" - }, - { - intro: "Pour cela, rendez-vous sur la page suivante et connectez-vous avec votre compte Google:
https://www.google.com/maps/timeline. Vous pourrez alors revenir sur l'application TimeLine EDB." + },{ + intro: "Pour cela, cliquez sur l'image suivante et connectez-vous avec votre compte Google.
" },{ intro: "Si des données se sont bien affichées, il va falloir les télécharger depuis le serveur de Google afin de les insérer dans l'application." },{ - intro: "Pour se faire, allez sur la page suivante, laissez les options par défaut, et cliquez sur Suivant." + intro: "Pour ce faire, cliquez sur l'image suivante, laissez les réglages par défaut (comme dans l'image) et cliquez sur suivant. " },{ - intro: 'Vous pouvez alors cliquer sur "Créér une archive" et attendre que le fichier zip vous soit proposé en téléchargement.' + intro: "Vous pouvez alors cliquer sur \"Créér une archive\"." + },{ + intro: "Et attendre que le fichier zip vous soit proposé, et le télécharger." },{ intro: "Une fois ce zip téléchargé, vous pourrez l'entrer dans l'application." - }, - { + },{ element: '#userData', intro: "En cliquant sur ce menu." - }, - { + },{ intro: "Tous les graphiques et la carte se mettront alors à jour avec vos propres données. Bonne exploration avec TimeLine EDB !" },{ element: "#userDataHelp", @@ -131,8 +116,4 @@ function userDataIntro(){ }); introUserData.start(); -} - -window.onload = function() { - checkCookieIntro(); } \ No newline at end of file diff --git a/www/ajax-loader-bar.gif b/www/ajax-loader-bar.gif new file mode 100644 index 0000000..09d621e Binary files /dev/null and b/www/ajax-loader-bar.gif differ diff --git a/www/analytics.js b/www/analytics.js new file mode 100644 index 0000000..1db1d67 --- /dev/null +++ b/www/analytics.js @@ -0,0 +1,9 @@ +(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date(); + a=s.createElement(o), m=s.getElementsByTagName(o)[0]; + a.async=1; + a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-44910337-8', 'auto'); + ga('send', 'pageview'); diff --git a/www/favicon.png b/www/favicon.png new file mode 100644 index 0000000..7743b9b Binary files /dev/null and b/www/favicon.png differ diff --git a/www/img/google_takeout_download.png b/www/img/google_takeout_download.png new file mode 100644 index 0000000..9a0d742 Binary files /dev/null and b/www/img/google_takeout_download.png differ diff --git a/www/img/google_takeout_export.png b/www/img/google_takeout_export.png new file mode 100644 index 0000000..5cde248 Binary files /dev/null and b/www/img/google_takeout_export.png differ diff --git a/www/img/google_takeout_home.png b/www/img/google_takeout_home.png new file mode 100644 index 0000000..541d0d7 Binary files /dev/null and b/www/img/google_takeout_home.png differ diff --git a/www/img/google_timeline_home.png b/www/img/google_timeline_home.png new file mode 100644 index 0000000..3f844a4 Binary files /dev/null and b/www/img/google_timeline_home.png differ diff --git a/www/test.png b/www/test.png new file mode 100644 index 0000000..aefd13b Binary files /dev/null and b/www/test.png differ