Skip to content

Commit 1d03ac1

Browse files
author
user
committed
first commit
0 parents  commit 1d03ac1

File tree

7,547 files changed

+758
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

7,547 files changed

+758
-0
lines changed

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
### Introduction
2+
3+
This shiny application is a real time bike station information. It provides information on more than 30 bike self-service stations around the world.
4+
This application is composed of an interface file (**ui.R**), a server file (**server.R**), and folder where we can find data (**www**).
5+
6+
7+
You can see the shiny application in action [here](http://blog.rdata.lu/visualization/bike/)
8+
9+
10+
11+
12+
### Before running the code
13+
14+
Before running the code you should run data_dist.R on R to generate, the file closest_stations.RDS (this file is to large to be save in GitHub). Then you will be abble to run the shiny app.
15+
The offline version is veloh_API_offline.R, if you have any difficulty to make it run offline, contact me :)
16+
The shiny server version is ui.R and Server.R
17+
18+
Don't forget to change the path (provide the complete path) depending on where you have saved the file :)
19+
20+
21+
Enjoy this application and if you have any issues to make it work correctly, you can always watch my [tutorial video](https://youtu.be/GHRZaiYh2Ac) or [contact me](mailto:kevin.rosamont@rdata.lu).

data_dist.R

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
if(!require('tidyverse')) install.packages('tidyverse')
2+
library(tidyverse)
3+
4+
path= "/www/"
5+
closest_stations = readRDS(paste0(path,"closest_stations.RDS"))
6+
stop = nrow(closest_stations)/5
7+
stop = (1:stop)*5
8+
9+
#toMin = function(x){
10+
# if(x>60){
11+
# min = floor(x/60)
12+
# sec = substr(x-(60*min),1,2)
13+
# res = paste0(min,".",sec, " min")
14+
# }else{
15+
# res = paste0(x, " sec")
16+
# }
17+
18+
# return(res)
19+
#}
20+
21+
22+
files <- list.files(path = "/www/destination/", pattern = "destination*")
23+
24+
df_files = data_frame(file=files) %>%
25+
mutate(stop_files = as.numeric(gsub("[^0-9]","", files))) %>%
26+
arrange(stop_files)
27+
28+
files = df_files$file
29+
stop_files = df_files$stop_files
30+
31+
missing = stop[!stop %in% stop_files]
32+
missing
33+
34+
too_much = stop_files[!stop_files %in% stop]
35+
too_much
36+
setwd("/www/destination/")
37+
38+
mapper = function(x){
39+
l=length(x)
40+
1:l %>%
41+
map(function(y){
42+
#If there is 2 values, choose the smalllest
43+
time=min(x[[y]]$Time[[length(x[[y]]$Time)]])
44+
dist=min(x[[y]]$Distance[[length(x[[y]]$Distance)]])
45+
cbind(time, dist)
46+
})
47+
}
48+
49+
time_dist = function(x){
50+
readRDS(x) %>% mapper %>% reduce(rbind)
51+
}
52+
53+
df_dist_time = files %>%
54+
map(time_dist) %>%
55+
reduce(rbind) %>%
56+
as.data.frame( )
57+
58+
nrow(df_dist_time)==nrow(closest_stations)
59+
60+
closest_stations = cbind.data.frame(closest_stations, df_dist_time) %>%
61+
select(number, name, contract_name, dest_lat, dest_lng,
62+
origin_lat, origin_lng, time, dist)
63+
64+
65+
saveRDS(closest_stations , "/Users/user/Desktop/GitHub/shiny_bike/www/closest_stations_full.RDS")
66+

server.R

+251
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
#options(shiny.fullstacktrace=TRUE) #usefull for the bugs
2+
#options(shiny.error = browser) #usefull for the bugs
3+
library(shiny)
4+
library(shinydashboard)
5+
library(leaflet)
6+
library(jsonlite)
7+
library(curl) # make the jsonlite suggested dependency explicit
8+
library(tidyverse)
9+
10+
#Read data
11+
contract =readRDS("www/contract.RDS" )
12+
13+
14+
#Read station data with the distance (thanks google for computing the distance time)
15+
closest_stations = readRDS("www/closest_stations_full.RDS")
16+
closest_stations = closest_stations %>%
17+
mutate(name = trimws(gsub("[0-9-]"," ", name)))
18+
19+
#To get the API key: https://developer.jcdecaux.com/#/opendata/vls?page=getstarted
20+
#Download veloh location through the API
21+
getvelohdata <- function(contract_name) {
22+
url <- paste0("https://api.jcdecaux.com/vls/v1/stations?contract=",contract_name,"&apiKey=YOUR_API_KEY")
23+
df_api = jsonlite::fromJSON(url)
24+
position.lng = df_api$position$lng
25+
position.lat = df_api$position$lat
26+
df_api = cbind(df_api %>% select(-position), position.lng, position.lat) %>%
27+
mutate( name = trimws(gsub("[0-9-]"," ", name)),
28+
#address = paste0("<a href='https://www.google.com/maps/search/?api=1&query=",position.lat,",",position.lng,"' target='_blank'>",address,"</a>"),
29+
address = paste0("<a href='https://www.openstreetmap.org/?mlat=",position.lat,"&mlon=",position.lng,"#map=15/",position.lat,"/",position.lng,"' target='_blank'>",address,"</a>"),
30+
title_attrib = paste0("Name: ",name,"<br>Adress: ",address,"<br>Available: ",available_bikes,"/",bike_stands),
31+
color= ifelse(status != "OPEN", "red","blue"),
32+
available_bike_stands = ifelse(color=="red",0,available_bikes))
33+
return(df_api)
34+
35+
}
36+
37+
#Convert time to minute and seconds
38+
toMin = function(x){
39+
if(x>60){
40+
min = floor(x/60)
41+
sec = substr(x-(60*min),1,2)
42+
res = paste0(min,".",sec, " min")
43+
}else{
44+
res = paste0(x, " sec")
45+
}
46+
47+
return(res)
48+
}
49+
50+
function(input, output, session) {
51+
52+
53+
# Locations of all active vehicles
54+
vehicleLocations <- reactive({
55+
# Get interval (minimum 90 seconds)
56+
interval <- max(as.numeric(input$interval), 90)
57+
# Invalidate this reactive after the interval has passed, so that data is
58+
# fetched again.
59+
invalidateLater(interval * 1000, session)
60+
getvelohdata(input$ncity)
61+
})
62+
63+
# Get time that locations were updated
64+
lastUpdateTime <- reactive({
65+
vehicleLocations() # Trigger this reactive when vehicles locations are updated
66+
Sys.time()
67+
})
68+
69+
# Number of seconds since last update
70+
output$timeSinceLastUpdate <- renderUI({
71+
# Trigger this every 5 seconds
72+
invalidateLater(5000, session)
73+
74+
p(
75+
class = "text-muted",
76+
"Data refreshed ",
77+
round(difftime(Sys.time(), lastUpdateTime(), units="secs")),
78+
" seconds ago."
79+
)
80+
81+
})
82+
83+
output$localTime <- renderUI({
84+
if(input$ncity=="Brisbane"){
85+
timezone="Australia/Brisbane"
86+
}else if(input$ncity=="Dublin"){
87+
timezone="Europe/Dublin"
88+
}else if(input$ncity=="Vilnius"){
89+
timezone="Europe/Vilnius"
90+
}else if(input$ncity=="Toyama"){
91+
timezone="Asia/Tokyo"
92+
}else if(input$ncity=="Kazan"){
93+
timezone="Europe/Moscow"
94+
}else{
95+
timezone="Europe/Paris"
96+
}
97+
hour <- Sys.time()
98+
time = format(hour,tz=timezone)
99+
time = substr(time,12,16)
100+
p(class = "text-muted",
101+
br(),
102+
"Source data updates every 90 seconds.",
103+
br(),br(),br(),
104+
"Last update was at ", time,br(), " (Current local time in", input$ncity,")."
105+
)
106+
})
107+
108+
output$Box1 = renderUI(
109+
selectInput('nstation', 'Station:', vehicleLocations()$name)
110+
)
111+
112+
output$Box2 = renderUI({
113+
selectizeInput('ncity', 'City:', contract$Contract, selected = "Luxembourg",
114+
options = list(
115+
valueField = 'url',
116+
labelField = 'name',
117+
searchField = 'name',
118+
options = list(),
119+
create = FALSE,
120+
#To add the flag next to the countries
121+
render = I("{
122+
option: function(item, escape) {
123+
return '<div>' +
124+
'<img src=\"image/flag/' +
125+
(item.name) +
126+
'.png\" width=20 />' +
127+
'&nbsp; &nbsp; &nbsp;' +
128+
escape(item.name) +
129+
'</div>';
130+
}
131+
}")))
132+
})
133+
134+
output$velohTable <- renderUI({
135+
locations <- vehicleLocations()
136+
137+
station = locations %>%
138+
filter(name==input$nstation)
139+
140+
closest = closest_stations %>%
141+
filter(contract_name==input$ncity & origin_lat==station$position.lat &
142+
origin_lng == station$position.lng) %>%
143+
arrange(dist) %>%
144+
.[1:5,]
145+
destination = left_join(closest, locations, by=c("name")) %>%
146+
select(name, available_bikes, bike_stands, address, dist, time, status) %>%
147+
mutate(time = toMin(time), dist= paste0(dist," m"), name=trimws(gsub("[0-9-]"," ", name)),
148+
status=ifelse(status=="OPEN", "#46abdd", "#d13d2d"),
149+
available_bikes= paste0(available_bikes,"/", bike_stands))
150+
151+
152+
# Create a Bootstrap-styled table
153+
tags$table(class = "table",
154+
tags$h4(paste0("THE 5 Nearest stations to pick a bike from: ",input$nstation),
155+
id='title_tb'),
156+
tags$thead(
157+
tags$tr(
158+
tags$th(id="th_name","Station Names"),
159+
tags$th(id="th_avail","Availables"),
160+
tags$th(id="th_add","Address"),
161+
tags$th(id="th_dist","Distance"),
162+
tags$th(id="th_time","Time")
163+
)),
164+
tags$tbody(
165+
tags$tr(
166+
tags$td(class="td_name", tags$span(style = sprintf(
167+
"width:0.9em; height:0.9em; background-color:%s; display:inline-block;
168+
-webkit-border-radius: 0.6em; -moz-border-radius: 0.6em; border-radius: 0.6em;",
169+
destination$status[1]
170+
)),destination$name[1]),
171+
tags$td(class="td_avail", destination$available_bikes[1]),
172+
tags$td(class="td_add", HTML(destination$address[1])),
173+
tags$td(class="td_dist", destination$dist[1]),
174+
tags$td(class="td_time", destination$time[1])
175+
176+
),
177+
tags$tr(
178+
tags$td(class="td_name", tags$span(style = sprintf(
179+
"width:0.9em; height:0.9em; background-color:%s; display:inline-block;
180+
-webkit-border-radius: 0.6em; -moz-border-radius: 0.6em; border-radius: 0.6em;",
181+
destination$status[2]
182+
)),destination$name[2]),
183+
tags$td(class="td_avail", destination$available_bikes[2]),
184+
tags$td(class="td_add", HTML(destination$address[2])),
185+
tags$td(class="td_dist", destination$dist[2]),
186+
tags$td(class="td_time", destination$time[2])
187+
),
188+
tags$tr(
189+
tags$td(class="td_name", tags$span(style = sprintf(
190+
"width:0.9em; height:0.9em; background-color:%s; display:inline-block;
191+
-webkit-border-radius: 0.6em; -moz-border-radius: 0.6em; border-radius: 0.6em;",
192+
destination$status[3]
193+
)),destination$name[3]),
194+
tags$td(class="td_avail", destination$available_bikes[3]),
195+
tags$td(class="td_add", HTML(destination$address[3])),
196+
tags$td(class="td_dist", destination$dist[3]),
197+
tags$td(class="td_time", destination$time[3])
198+
),
199+
tags$tr(
200+
tags$td(class="td_name", tags$span(style = sprintf(
201+
"width:0.9em; height:0.9em; background-color:%s; display:inline-block;
202+
-webkit-border-radius: 0.6em; -moz-border-radius: 0.6em; border-radius: 0.6em;",
203+
destination$status[4]
204+
)),destination$name[4]),
205+
tags$td(class="td_avail", destination$available_bikes[4]),
206+
tags$td(class="td_add", HTML(destination$address[4])),
207+
tags$td(class="td_dist", destination$dist[4]),
208+
tags$td(class="td_time", destination$time[4])
209+
),
210+
tags$tr(
211+
tags$td(class="td_name", tags$span(style = sprintf(
212+
"width:0.9em; height:0.9em; background-color:%s; display:inline-block;
213+
-webkit-border-radius: 0.6em; -moz-border-radius: 0.6em; border-radius: 0.6em;",
214+
destination$status[5]
215+
)),destination$name[5]),
216+
tags$td(class="td_avail", destination$available_bikes[5]),
217+
tags$td(class="td_add", HTML(destination$address[5])),
218+
tags$td(class="td_dist", destination$dist[5]),
219+
tags$td(class="td_time", destination$time[5])
220+
)
221+
)
222+
)
223+
})
224+
225+
226+
output$velohmap <- renderLeaflet({
227+
locations <- vehicleLocations()
228+
229+
station = locations %>%
230+
filter(locations$name==input$nstation)
231+
232+
locations = locations %>%
233+
mutate(color=ifelse(name==station$name,"green",color))
234+
235+
#The format of the icons
236+
icons <- awesomeIcons(
237+
icon = 'ios-close',
238+
iconColor = 'white',
239+
library = 'ion',
240+
markerColor = locations$color,
241+
fontFamily = "serif",
242+
text= as.character(locations$available_bike_stands)
243+
)
244+
leaflet(locations) %>% setView(lng = station$position.lng, lat = station$position.lat, zoom = 14) %>%
245+
addTiles() %>%
246+
#Nice function that allows you to do a lot of things, I recommand to read the documentation.
247+
addAwesomeMarkers(~position.lng, ~position.lat,
248+
label=~available_bikes,
249+
icon=icons, popup = ~title_attrib)
250+
})
251+
}

ui.R

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
library(shiny)
2+
library(shinydashboard)
3+
library(leaflet)
4+
#This is code manage the shiny application interface
5+
header <- dashboardHeader(
6+
title = div(tags$span("JCDecaux Bike Sevices ",tags$span(" ",id="white_space"), tags$img(src="image/share_bike.png", id="logo_title"))
7+
)
8+
)
9+
body <- dashboardBody(
10+
fluidRow(
11+
column(width = 12,
12+
box(width = NULL, solidHeader = TRUE,
13+
leafletOutput("velohmap", height = 500)
14+
),
15+
box(width = NULL,
16+
uiOutput("velohTable")
17+
)
18+
),
19+
absolutePanel(id = "controls", class = "panel panel-default", fixed = TRUE,
20+
draggable = TRUE, top = 60, left = "auto", right = 20, bottom = "auto",
21+
width = 330, height = "auto",
22+
23+
h4("STATIONS TO PICK A BIKE"),
24+
25+
box(width = NULL, status = "warning",
26+
uiOutput("Box1"),
27+
uiOutput("Box2"),
28+
uiOutput("timeSinceLastUpdate"),
29+
uiOutput("localTime")
30+
)
31+
)
32+
)
33+
)
34+
35+
shinyUI(dashboardPage(
36+
header,
37+
dashboardSidebar(
38+
tags$head(
39+
tags$link(rel = "stylesheet", type = "text/css", href = "stylesheet.css")
40+
),
41+
disable = TRUE),
42+
body)
43+
)

0 commit comments

Comments
 (0)