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
+ ' ' +
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
+ }
0 commit comments