<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ rshiny - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ rshiny - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 04:58:31 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/rshiny/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Weather App with R Shiny ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you’ll learn how to build a weather app in R. Really – a weather app, in R? Wait, hear me out. When you think of R, you probably imagine someone wearing chunky thick prescription glasses and devouring a book. You know, a statisticia... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-weather-app-with-r-shiny/</link>
                <guid isPermaLink="false">67570d22e8032cfb3def7b4e</guid>
                
                    <category>
                        <![CDATA[ rshiny ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Semantic UI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ R Language ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Elabonga Atuo ]]>
                </dc:creator>
                <pubDate>Mon, 09 Dec 2024 15:30:42 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733174501446/a177379f-3c32-424a-9fbe-6608310f2ea6.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you’ll learn how to build a weather app in R. Really – a weather app, in R? Wait, hear me out.</p>
<p>When you think of R, you probably imagine someone wearing chunky thick prescription glasses and devouring a book. You know, a statistician dealing with complex models, an insane amount of mathematical equations, and copious amounts of data.</p>
<p>But R is far more than just a tool for statistics. It shines when you need to turn raw data into actionable insights and present those insights in a clear, engaging way.</p>
<p>With frameworks like Shiny, R takes this one step further, enabling you to create fully interactive web apps without having to worry about frontends, backends, or learning an entirely new programming language.</p>
<p>In this tutorial, you will create a simple weather app that fetches data from an API and displays the results in a good-looking app.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-project-overview">Project Overview</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-api-keys-storage-and-retrieval">API Keys: Storage and Retrieval</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-your-first-api-call">How to Make Your First API Call</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-shiny-app">How to Build the Shiny App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-project-overview">Project Overview</h2>
<p>Here’s what we’re going to be building:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733341336823/dd605385-5531-43c5-924d-dde24b38846b.gif" alt="The R Shiny weather app demo" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>For the weather app to work, you will need to make two separate API calls. We’ll use the One Call API 3.0 to update weather data and the OpenWeather API for geocoding. You can get your API Key <a target="_blank" href="https://openweathermap.org/api">here</a>. Just keep in mind that if this is your first time signing up for an API key, activation may take up to 24 hours.</p>
<p>The weather app will take the location/city from user input. The input will then be geocoded by making the call to OpenWeather API. Then, from its response, the coordinates (latitude and longitude) will be extracted. The coordinates will be used as query arguments for the One Call API call to obtain the weather data in JSON format.</p>
<h3 id="heading-prerequisites">Prerequisites:</h3>
<p>To follow along with this tutorial, you will need:</p>
<ul>
<li><p>R programming knowledge</p>
</li>
<li><p>HTML and a bit of JavaScript knowledge</p>
</li>
<li><p>R Studio installed</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733172415724/c4f884f6-b583-4f13-b0f8-eb564ab6531f.png" alt="Weather Update API Flow" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Create a folder in your desired directory. Set and confirm the project folder as the working directory using the following command in the R console:</p>
<pre><code class="lang-r">setwd(<span class="hljs-string">"path/to/your/project/file"</span>)
getwd()
</code></pre>
<p>Create a project in the set path using the following command:</p>
<pre><code class="lang-r"><span class="hljs-comment">#create R project</span>
usethis::create_project(path = <span class="hljs-string">"."</span>, open = <span class="hljs-literal">FALSE</span>)
</code></pre>
<p>You should have a folder structure that looks like this.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733166096334/93e004da-4449-4cb4-8ddd-d3082e5687d8.png" alt="project folder structure" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Create an R file in the root directory and save it as <code>app.R</code>. All your R code will be contained here.</p>
<p>Install and load the following libraries that you are going to work with:</p>
<pre><code class="lang-r"><span class="hljs-keyword">library</span>(shiny)
<span class="hljs-keyword">library</span>(bslib)
<span class="hljs-keyword">library</span>(shinyjs)
<span class="hljs-keyword">library</span>(httr2)
<span class="hljs-keyword">library</span>(lubridate)
<span class="hljs-keyword">library</span>(shiny.semantic)
</code></pre>
<h2 id="heading-api-keys-storage-and-retrieval">API Keys: Storage and Retrieval</h2>
<p>Storing your credentials in a location separate from your scripts and global environment is a good practice. This ensures security, scalability, and flexibility, especially when working in shared or production environments. The <code>.Renviron</code> file best serves that purpose.</p>
<p>Open and edit your <code>.Renviron</code> file in the following way:</p>
<pre><code class="lang-r"><span class="hljs-comment">#open and edit .Renviron</span>
usethis::edit_r_environ(scope=c(<span class="hljs-string">"project"</span>)
</code></pre>
<p>The scope argument set to <code>project</code> sets up the <code>.Renviron</code> specifically to your project. In the newly opened file, add your API key as follows:</p>
<pre><code class="lang-r">OPENWEATHERAPIKEY=<span class="hljs-string">"yourapikey"</span>
</code></pre>
<h2 id="heading-how-to-make-your-first-api-call">How to Make Your First API Call</h2>
<p>You will be using the httr2 library (built based on httr) to obtain data from the API. It grants you more control over how you make requests to the web.</p>
<h3 id="heading-make-the-api-key-accessible-in-the-script">Make the API Key accessible in the script</h3>
<p>First, you’ll need to securely access and store the API key in the script without hardcoding it. You can do that like this:</p>
<pre><code class="lang-r"><span class="hljs-comment">#access API keys in script</span>
readenviron(<span class="hljs-string">".Renviron"</span>)
api_key = Sys.getenv(<span class="hljs-string">"OPENWEATHERAPIKEY"</span>)
</code></pre>
<h3 id="heading-define-the-geocoding-function">Define the Geocoding Function</h3>
<p>You will create a function that takes a location and an API key as inputs, sends a request to the OpenWeather geocoding API, and returns the coordinates of the specified location.</p>
<p>Start by creating a request. The pipe (<code>|&gt;</code>) operator facilitates the chaining of HTTP requests step by step in a clear and readable manner. The geocoding URL takes two parameters: location, denoted by <code>q</code>, and the API key, denoted by <code>app_id</code>. The <code>req_url_query()</code> function appends these parameters to the query.</p>
<p>Chain the query to perform the request and fetch action, and finally obtain the response in JSON format using the second to last line.</p>
<pre><code class="lang-r"><span class="hljs-comment"># Geocoding URL</span>
geocoding_url &lt;- <span class="hljs-string">"https://api.openweathermap.org/data/2.5/weather"</span>
geocode &lt;- <span class="hljs-keyword">function</span>(location, api_key) {
  request(geocoding_url) |&gt; 
    req_url_query(`q` = location, `appid` = api_key) |&gt; 
    req_perform() |&gt; 
    resp_body_json() |&gt;
    coordinates()
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733342454801/feed01b2-a7a1-4c69-8297-2dcfdc8ec39f.png" alt="A sample response to the geocoding API" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-define-the-coordinate-extracting-function">Define the coordinate-extracting function</h3>
<p>The <code>coordinates()</code> function is a helper function that extracts the latitude and longitude values from the JSON response. A quick inspection of the JSON response reveals the coordinate's position. The JSON object is simply a long list of lists and you can access elements by subsetting it.</p>
<p>A blank data body would imply that the city/location is unavailable, and you’d get the message <em>"No such city exists!"</em>. If the JSON contains an element, the length would be more than 0 – it is a list after all.</p>
<pre><code class="lang-r">coordinates &lt;- <span class="hljs-keyword">function</span>(body) {
  <span class="hljs-keyword">if</span>(length(body) != <span class="hljs-number">0</span>) { 
    lat &lt;- body$coord$lat
    lng &lt;- body$coord$lon
    town &lt;- body$name
    c(lat, lng, town)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-string">"No such city exists!"</span>
  }
}
</code></pre>
<h3 id="heading-define-the-weather-update-function">Define the weather-update function</h3>
<p>You will create a function that sends a request to the OpenWeather API with specified query parameters, handles errors using a predefined function, and returns the parsed JSON response containing the weather data.</p>
<p>As implemented in the geocoding function, start by creating a request and adding the necessary query parameters using the <code>req_url_query()</code> function. The <code>openweather_json()</code> function accepts two main arguments:</p>
<ul>
<li><p><code>api_key</code>: This is a required argument used for authentication with the OpenWeather API matched by position.</p>
</li>
<li><p><code>...</code>: This represents optional keyword arguments that you can use to customize the query. You can pass as many additional parameters as needed, provided they are specified as named arguments.</p>
</li>
</ul>
<pre><code class="lang-r">openweather_json &lt;- <span class="hljs-keyword">function</span>(api_key, <span class="hljs-keyword">...</span>) { 
  request(current_weather_url) |&gt; 
    req_url_query(<span class="hljs-keyword">...</span>, `appid` = api_key, `units` = <span class="hljs-string">"metric"</span>) |&gt; 
    req_error(body = openweather_error_body) |&gt;
    req_perform() |&gt; 
    resp_body_json()
}
</code></pre>
<h3 id="heading-error-handling-extracting-and-managing-status-codes">Error Handling: Extracting and Managing Status Codes</h3>
<p>You will create an error-handling function that extracts non-200 status codes from a response and defines how to manage them. The structure of this function depends on how the API reports errors and where the relevant information is stored.</p>
<h4 id="heading-define-the-weather-update-error-body">Define the weather-update error body</h4>
<p>The <code>req_error()</code> in <code>openweather_json()</code> introduces a new concept: error handling. API requests may throw exceptions, and getting the status codes helps you know what message to show the user and how to resolve it.</p>
<p>Create an error body which is a function that captures the error code if the status code is not 200 (which means everything is OK).</p>
<p>The function takes a response and extracts the status response stored in the JSON response at the <code>$message</code> sublist. The underscore <code>(_)</code>is a placeholder for the JSON object.</p>
<pre><code class="lang-r">openweather_error_body &lt;- <span class="hljs-keyword">function</span>(resp) {
  resp |&gt; resp_body_json() |&gt; _$message 
}
</code></pre>
<h4 id="heading-define-the-geocode-error-body">Define the geocode error body</h4>
<p>This error body function will prove useful in the Shiny App. This is a simple walkthrough.</p>
<p>The <code>req_error()</code> function allows you to customize how response errors are handled. Its <code>is_error</code> argument determines whether a given response should be considered an error. By setting <code>is_error</code> to <code>\(resp) FALSE</code> (an anonymous function that always returns FALSE), all responses, regardless of the status code, are treated as successful. This prevents the app from exiting due to non-200 status codes.</p>
<p>With this setup, you can extract the status code from the response body and pipe it into the <code>resp_status()</code> function to retrieve the exact code.</p>
<pre><code class="lang-r">openstreetmap_error_body &lt;- <span class="hljs-keyword">function</span>(location, api_key) {
  resp &lt;- request(geocoding_url) |&gt; 
    req_url_query(`q` = location, `appid` = api_key) |&gt; 
    req_error(is_error = \(resp) <span class="hljs-literal">FALSE</span>) |&gt;
    req_perform() |&gt;  resp_status()
  resp
}
</code></pre>
<h2 id="heading-how-to-build-the-shiny-app">How to Build the Shiny App</h2>
<p>Now that you have nailed down how to obtain data from the API, it’s time to render the results in an interpretable and interactive format. For this, you will use Shiny. Shiny is a framework that allows you to create interactive web apps.</p>
<p>A Shiny App is made up of two components:</p>
<ul>
<li><p>The UI: what the user interacts with. It defines the layout and appearance of the app.</p>
</li>
<li><p>The server: contains the app’s logic and behaviour.</p>
</li>
</ul>
<h3 id="heading-building-the-shiny-ui">Building the Shiny UI</h3>
<p>Shiny UI provides a collection of elements that allow users to input data, make selections, and trigger events seamlessly.</p>
<p>You will include a <code>textInput</code> element that takes in the location and the weather data will be fetched and rendered upon submission. The <code>input_task_button</code> button prevents the user from clicking when an API call is in progress. The other elements are output elements where the weather data will be displayed and a mode-switching button.</p>
<h4 id="heading-styling-the-shiny-app">Styling the Shiny app</h4>
<p>You can use <code>shiny.semantic</code>, a library built on top of Fomantic-UI, to style your Shiny dashboard. Fomantic-UI is a front-end framework that provides a rich collection of pre-styled HTML components like buttons, modals, form inputs, and more. It simplifies UI design by allowing developers to create visually appealing and responsive interfaces without needing extensive custom CSS or HTML knowledge.</p>
<p>Fomantic-UI styling is applied by wrapping elements in their corresponding classes, which define their behavior and appearance.</p>
<p>A grid in Fomantic-UI is a flexible layout system used to organize content. It acts as a canvas that divides the layout into rows (horizontally aligned) and columns (vertically aligned). A root grid can contain up to 16 columns, making it ideal for creating structured and responsive designs.</p>
<p>To specify a column's width, you append classes like wide and the size (a number from 1 to 16) to represent its span. The total width of all columns in a row should sum up to 16.</p>
<p>A segment groups related content, while a card displays detailed, content-rich items, such as a user's social media profile. Dividers are visual elements used to separate sections or content within a layout.</p>
<p>For the weather app, first create a div of class <code>grid</code> within which you’ll nest the various elements.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733137762676/12d5695c-2ed7-4606-8267-44243c2bee57.png" alt="semantic page layout demo" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h5 id="heading-search-bar-section"><strong>Search bar section</strong></h5>
<p>Divide the grid into sixteen columns and create a segment that groups elements in the search bar section. Add a theme toggle button, location input that takes in user input, a search button for submitting the location to the API, and a notification button, defining their width by the column size.</p>
<pre><code class="lang-r">div(class = <span class="hljs-string">"sixteen wide column"</span>,
          div(class = <span class="hljs-string">"ui segment"</span>,
              div(class = <span class="hljs-string">"ui grid"</span>,
                  div(class = <span class="hljs-string">"two wide column"</span>,
                      button(
                        class = <span class="hljs-string">"ui button icon basic"</span>,
                        input_id = <span class="hljs-string">"darkmode"</span>,
                        label = <span class="hljs-literal">NULL</span>,
                        icon = icon(<span class="hljs-string">"moon icon"</span>)
                      )
                  ),
                  div(class = <span class="hljs-string">"ten wide column"</span>,
                      textInput(
                        <span class="hljs-string">"location"</span>,
                        label = <span class="hljs-literal">NULL</span>,
                        placeholder = <span class="hljs-string">"Search for your preferred city"</span>
                      )
                  ),
                  div(class = <span class="hljs-string">"two wide column"</span>,
                      tags$div(
                        class = <span class="hljs-string">"ui button"</span>,
                        id = <span class="hljs-string">"my-custom-button"</span>,
                        input_task_button(<span class="hljs-string">"search"</span>, label = <span class="hljs-string">"Search"</span>, icon = icon(<span class="hljs-string">"search"</span>))
                      )
                  ),
                  div(class = <span class="hljs-string">"two wide column"</span>,
                      actionButton(<span class="hljs-string">"show_alert"</span>, label = icon(<span class="hljs-string">"bell"</span>), class = <span class="hljs-string">"bell-no-alert"</span>),
                      textOutput(<span class="hljs-string">"alert_message"</span>)
                  )
              )
          )
      )
</code></pre>
<h5 id="heading-location-and-current-weather-section"><strong>Location and current weather section</strong></h5>
<p>Divide the grid into sixteen columns and nest another grid within the partitions that will host two columns.</p>
<p>Within the grid, define two columns. The first column is for time, location, and date data, and the second column will hold current weather data.</p>
<p>Then create card elements to hold each weather parameter, its unit of measurement, and the corresponding icon.</p>
<pre><code class="lang-r">div(class = <span class="hljs-string">"sixteen wide column"</span>,
          div(class = <span class="hljs-string">"ui equal-height-grid grid"</span>,
              div(class = <span class="hljs-string">"left floated center aligned four wide column"</span>,
                  div(class = <span class="hljs-string">"ui raised equal-height-two-segment segment"</span>,
                      style = <span class="hljs-string">"flex: 1;"</span>,
                      div(class = <span class="hljs-string">"column center aligned"</span>,
                          div(class = <span class="hljs-string">"ui hidden section divider"</span>),
                          span(class = <span class="hljs-string">"ui large text"</span>, textOutput(<span class="hljs-string">"city"</span>)),
                          div(class = <span class="hljs-string">"ui hidden section divider"</span>),
                          span(class = <span class="hljs-string">"ui big text"</span>, textOutput(<span class="hljs-string">"currentTime"</span>)),
                          div(class = <span class="hljs-string">"ui hidden section divider"</span>),
                          span(class = <span class="hljs-string">"ui large text"</span>, textOutput(<span class="hljs-string">"currentDate"</span>)),
                          div(class = <span class="hljs-string">"ui hidden section divider"</span>)
                      )
                  )
              ),
              div(class = <span class="hljs-string">"right floated center aligned twelve wide column"</span>,
                  div(class = <span class="hljs-string">"ui raised segment"</span>,
                      div(class = <span class="hljs-string">"ui horizontal equal width segments"</span>,
                          div(class = <span class="hljs-string">"ui equal-height-two-segment segment"</span>,
                              style = <span class="hljs-string">"flex: 3;"</span>,
                              div(class = <span class="hljs-string">"column"</span>,
                                  span(class = <span class="hljs-string">"ui big text centered"</span>, textOutput(<span class="hljs-string">"currentTemp"</span>)),
                                  textOutput(<span class="hljs-string">"feelsLike"</span>),
                                  card(
                                    class = <span class="hljs-string">"ui mini"</span>,
                                    div(class = <span class="hljs-string">"content"</span>, icon(class = <span class="hljs-string">"large sun"</span>),
                                        div(class = <span class="hljs-string">"sub header"</span>, <span class="hljs-string">"Sunrise"</span>),
                                        div(class = <span class="hljs-string">"description"</span>, textOutput(<span class="hljs-string">"sunriseTime"</span>))
                                    )
                                  ),
                                  card(
                                    class = <span class="hljs-string">"ui mini"</span>,
                                    div(class = <span class="hljs-string">"content"</span>, icon(class = <span class="hljs-string">"large moon"</span>),
                                        div(class = <span class="hljs-string">"sub header"</span>, <span class="hljs-string">"Sunset"</span>),
                                        div(class = <span class="hljs-string">"description"</span>, textOutput(<span class="hljs-string">"sunsetTime"</span>))
                                    )
                                  )
                              )
                          ),
                          div(class = <span class="hljs-string">"ui segment"</span>,
                              style = <span class="hljs-string">"flex: 3;"</span>,
                              div(
                                class = <span class="hljs-string">"column center aligned"</span>,
                                div(class = <span class="hljs-string">"ui hidden divider"</span>),
                                htmlOutput(<span class="hljs-string">"currentWeatherIcon"</span>),
                                span(class = <span class="hljs-string">"ui large text"</span>, textOutput(<span class="hljs-string">"currentWeatherDescription"</span>))
                              )
                          ),
                          div(class = <span class="hljs-string">"ui segment"</span>,
                              style = <span class="hljs-string">"flex: 3;"</span>,
                              div(class = <span class="hljs-string">"column"</span>,
                                  card(
                                    class = <span class="hljs-string">"ui tiny"</span>,
                                    div(class = <span class="hljs-string">"content"</span>, icon(class = <span class="hljs-string">"big tint"</span>),
                                        div(class = <span class="hljs-string">"sub header"</span>, <span class="hljs-string">"Humidity"</span>),
                                        div(class = <span class="hljs-string">"description"</span>, textOutput(<span class="hljs-string">"currentHumidity"</span>))
                                    )
                                  ),
                                  card(
                                    class = <span class="hljs-string">"ui tiny"</span>,
                                    div(class = <span class="hljs-string">"content"</span>, icon(class = <span class="hljs-string">"big tachometer alternate"</span>),
                                        div(class = <span class="hljs-string">"sub header"</span>, <span class="hljs-string">"Pressure"</span>),
                                        div(class = <span class="hljs-string">"description"</span>, textOutput(<span class="hljs-string">"currentPressure"</span>))
                                    )
                                  )
                              )
                          ),
                          div(class = <span class="hljs-string">"ui segment"</span>,
                              style = <span class="hljs-string">"flex: 3;"</span>,
                              div(class = <span class="hljs-string">"column center aligned"</span>,
                                  card(
                                    class = <span class="hljs-string">"ui tiny"</span>,
                                    div(class = <span class="hljs-string">"content"</span>, icon(class = <span class="hljs-string">"big wind"</span>),
                                        div(class = <span class="hljs-string">"sub header"</span>, <span class="hljs-string">"Wind Speed"</span>),
                                        div(class = <span class="hljs-string">"description"</span>, textOutput(<span class="hljs-string">"currentWindSpeed"</span>))
                                    )
                                  ),
                                  card(
                                    class = <span class="hljs-string">"ui tiny"</span>,
                                    div(class = <span class="hljs-string">"content"</span>, icon(class = <span class="hljs-string">"big umbrella"</span>),
                                        div(class = <span class="hljs-string">"sub header"</span>, <span class="hljs-string">"UV Index"</span>),
                                        div(class = <span class="hljs-string">"description"</span>, textOutput(<span class="hljs-string">"currentUV"</span>))
                                    )
                                  )
                              )
                          )
                      )
                  )
              )
          )
      )
</code></pre>
<p><strong>Forecast section</strong></p>
<p>This section holds the forecasted data. Divide the grid into sixteen columns and nest another grid within the partitions hosting two columns.</p>
<p>Within the grid, define two columns. The first column holds the <em>5-Day Forecast</em> data. Separate the elements containing different values using rows. The second column contains <em>Hourly Forecast</em> data. Separate the elements containing different values using columns.</p>
<pre><code class="lang-r">      <span class="hljs-comment"># Forecast section</span>
      div(class = <span class="hljs-string">"sixteen wide column"</span>,
          div(class = <span class="hljs-string">"ui grid equal-height-grid"</span>,
              div(class = <span class="hljs-string">"left floated center aligned six wide column"</span>,
                  div(class = <span class="hljs-string">"ui raised segment special-segment equal-height-segment"</span>,
                      h4(<span class="hljs-string">"5 Days Forecast:"</span>),
                      div(class = <span class="hljs-string">"ui three column special-column grid"</span>,
                          <span class="hljs-comment"># Day forecasts</span>
                          div(class = <span class="hljs-string">"row"</span>,
                              div(class = <span class="hljs-string">"five wide column"</span>, textOutput(<span class="hljs-string">"dailyDtOne"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, textOutput(<span class="hljs-string">"dailyTempOne"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, htmlOutput(<span class="hljs-string">"dailyIconOne"</span>))
                          ),
                          div(class = <span class="hljs-string">"row"</span>,
                              div(class = <span class="hljs-string">"five wide column"</span>, textOutput(<span class="hljs-string">"dailyDtTwo"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, textOutput(<span class="hljs-string">"dailyTempTwo"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, htmlOutput(<span class="hljs-string">"dailyIconTwo"</span>))
                          ),
                          div(class = <span class="hljs-string">"row"</span>,
                              div(class = <span class="hljs-string">"five wide column"</span>, textOutput(<span class="hljs-string">"dailyDtThree"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, textOutput(<span class="hljs-string">"dailyTempThree"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, htmlOutput(<span class="hljs-string">"dailyIconThree"</span>))
                          ),
                          div(class = <span class="hljs-string">"row"</span>,
                              div(class = <span class="hljs-string">"five wide column"</span>, textOutput(<span class="hljs-string">"dailyDtFour"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, textOutput(<span class="hljs-string">"dailyTempFour"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, htmlOutput(<span class="hljs-string">"dailyIconFour"</span>))
                          ),
                          div(class = <span class="hljs-string">"row"</span>,
                              div(class = <span class="hljs-string">"five wide column"</span>, textOutput(<span class="hljs-string">"dailyDtFive"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, textOutput(<span class="hljs-string">"dailyTempFive"</span>)),
                              div(class = <span class="hljs-string">"three wide column"</span>, htmlOutput(<span class="hljs-string">"dailyIconFive"</span>))
                          )
                      )
                  )
              ),
              div(class = <span class="hljs-string">"right floated center aligned ten wide column"</span>,
                  div(class = <span class="hljs-string">"ui raised segment special-segment equal-height-segment"</span>,
                      h4(<span class="hljs-string">"Hourly Forecast:"</span>),
                      div(
                        class = <span class="hljs-string">"ui grid"</span>,
                        style = <span class="hljs-string">"display: flex; flex-direction: row; align-items: center; justify-content: space-around; flex-wrap: wrap; height: 100%;"</span>,
                        <span class="hljs-comment"># Hourly forecasts</span>
                        div(class = <span class="hljs-string">"column"</span>,
                            textOutput(<span class="hljs-string">"hourlyDtOne"</span>),
                            htmlOutput(<span class="hljs-string">"hourlyIconOne"</span>),
                            textOutput(<span class="hljs-string">"hourlyTempOne"</span>)
                        ),
                        div(class = <span class="hljs-string">"column"</span>,
                            textOutput(<span class="hljs-string">"hourlyDtTwo"</span>),
                            htmlOutput(<span class="hljs-string">"hourlyIconTwo"</span>),
                            textOutput(<span class="hljs-string">"hourlyTempTwo"</span>)
                        ),
                        div(class = <span class="hljs-string">"column"</span>,
                            textOutput(<span class="hljs-string">"hourlyDtThree"</span>),
                            htmlOutput(<span class="hljs-string">"hourlyIconThree"</span>),
                            textOutput(<span class="hljs-string">"hourlyTempThree"</span>)
                        ),
                        div(class = <span class="hljs-string">"column"</span>,
                            textOutput(<span class="hljs-string">"hourlyDtFour"</span>),
                            htmlOutput(<span class="hljs-string">"hourlyIconFour"</span>),
                            textOutput(<span class="hljs-string">"hourlyTempFour"</span>)
                        ),
                        div(class = <span class="hljs-string">"column"</span>,
                            textOutput(<span class="hljs-string">"hourlyDtFive"</span>),
                            htmlOutput(<span class="hljs-string">"hourlyIconFive"</span>),
                            textOutput(<span class="hljs-string">"hourlyTempFive"</span>)
                        )
                      )
                  )
              )
          )
      )
  )
</code></pre>
<h3 id="heading-building-the-shiny-server">Building the Shiny Server</h3>
<p>Each element in the UI section has an ID (unique identifier) that is used to manipulate what data/information will be displayed to it.</p>
<p>The <code>render*()</code> set of functions defines the visualization type while the <code>output$*</code> functions subset elements. These two are used to link the visual to the logic. Most elements will have data extracted from the JSON list, except for the weather icons (for which an external link as a source will be referenced).</p>
<h4 id="heading-reactivity">Reactivity</h4>
<p>Reactivity is what makes Shiny apps dynamic—outputs automatically update when their dependencies change.</p>
<p>Two key components of reactivity are reactives and observers. A reactive computes and returns a value based on its dependencies, while an observer monitors reactive values and runs code that causes side effects, like logging or updating a database.</p>
<p>To control reactivity, you can use <code>bindEvent()</code> to delay execution until a specific event occurs or <code>observeEvent()</code> to listen for a user action and trigger a code block. Together, these tools provide flexibility for managing app behavior.</p>
<h4 id="heading-the-server-code">The Server Code</h4>
<ol>
<li><code>location</code> <strong>reactive</strong></li>
</ol>
<p>The location reactive includes an if-else conditional block that defines what message to display depending on the status code. The query variable contains the city/location that will be geocoded to obtain coordinates. The flow is piped to <code>bindEvent()</code>. This ensures the geocoding API call is completed before another call can be made, which reduces unnecessary requests.</p>
<pre><code class="lang-r">location &lt;- reactive({
    query &lt;- input$location
    <span class="hljs-keyword">if</span>(openstreetmap_error_body(query, api_key) == <span class="hljs-string">"404"</span>){
      validate(<span class="hljs-string">"No such city/town exists. Check your spelling!"</span>)
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(openstreetmap_error_body(query, api_key) == <span class="hljs-string">"400"</span>){
      validate(<span class="hljs-string">"Bad request"</span>)
    }
    coords &lt;- geocode(query, api_key)
  }) %&gt;% bindEvent(input$search)
</code></pre>
<ol start="2">
<li><code>weather_data</code> <strong>reactive</strong></li>
</ol>
<p>The weather reactive combines a geocoding API call and a weather update API call using coordinates obtained and extracted from <code>location()</code>:</p>
<pre><code class="lang-r">  weather_data &lt;- reactive({
    loc &lt;- location()
    openweather_json(api_key, lat = loc[<span class="hljs-number">1</span>], lon = loc[<span class="hljs-number">2</span>])
  })
</code></pre>
<p>To access the JSON objects returned by the API call, you call the reactive as if it were a function. The specific values to be extracted can then be accessed by subsetting the JSON value.</p>
<pre><code class="lang-r"><span class="hljs-comment"># subsetting weather data.</span>
  output$city &lt;- renderText({
    location()[<span class="hljs-number">3</span>]
  })

  output$currentWeatherDescription &lt;- renderText({
    weather_data()$current$weather[[<span class="hljs-number">1</span>]]$description
  })
</code></pre>
<ol start="3">
<li><strong>Create a Parse Date function</strong></li>
</ol>
<p>All the time data in the JSON response, forecasted or current, is provided in UNIX format. To make this information user-friendly, it needs to be converted into a human-readable format. You can do this by creating a function that takes the time data as input and uses functions from the <code>lubridate</code> package to handle the conversion.</p>
<p>First, convert the timestamp element to a datetime object. Format the time item to a 12-hour clock system and a date item to include the day of the week, the date, and the month.</p>
<ul>
<li><p><code>%I</code>: Displays the hour in a 12-hour clock format (01-12).</p>
</li>
<li><p><code>%M</code>: Displays the minutes (00-59).</p>
</li>
<li><p><code>%p</code>: Adds the AM/PM indicator.</p>
</li>
</ul>
<p>The paste function concatenates the values. The function returns a vector containing date and time values to be extracted by subsetting.</p>
<pre><code class="lang-r">parse_date &lt;- <span class="hljs-keyword">function</span>(timestamp) {
  datetime &lt;- as_datetime(timestamp) 
  date &lt;- paste(weekdays(datetime), <span class="hljs-string">","</span>, day(datetime), months(datetime))
  time &lt;- format(as.POSIXct(datetime), format = <span class="hljs-string">"%I:%M %p"</span>)
  c(date, time)
}
</code></pre>
<ol start="4">
<li><strong>Add a modal to display error messages</strong></li>
</ol>
<p>The <code>location</code> reactive provides a way to handle errors. You can incorporate a modal to enhance the user experience by overlaying the page and disabling its content until the user completes a specified action whenever an error occurs.</p>
<p>You’ll add JavaScript to control when and how the modal shows.</p>
<p>Add two modals in the UI section, each featuring an explanation of the error (header) and an outline of the required action (content). The <code>action</code> class includes a button that enables the user to close the modal.</p>
<pre><code class="lang-r"><span class="hljs-comment"># modals - UI</span>
  div(id = <span class="hljs-string">"notFound"</span>, class = <span class="hljs-string">"ui modal"</span>,
      div(class = <span class="hljs-string">"header"</span>, <span class="hljs-string">"Location Not Found"</span>),
      div(class = <span class="hljs-string">"content"</span>, <span class="hljs-string">"No such city/town exists. Check your spelling!"</span>),
      div(class = <span class="hljs-string">"actions"</span>,
          div(class = <span class="hljs-string">"ui button"</span>, id = <span class="hljs-string">"closeNotFound"</span>, <span class="hljs-string">"OK"</span>))
  ),
  div(id = <span class="hljs-string">"badRequest"</span>, class = <span class="hljs-string">"ui modal"</span>,
      div(class = <span class="hljs-string">"header"</span>, <span class="hljs-string">"Invalid Request"</span>),
      div(class = <span class="hljs-string">"content"</span>, <span class="hljs-string">"Bad request. Please try again with valid details."</span>),
      div(class = <span class="hljs-string">"actions"</span>,
          div(class = <span class="hljs-string">"ui button"</span>, id = <span class="hljs-string">"closeBadRequest"</span>, <span class="hljs-string">"OK"</span>))
  )
</code></pre>
<p>Slightly adjust the location reactive to incorporate the modal. The commented-out code will be replaced with the JavaScript lines. The <code>runjs</code> function shows the modal depending on the error encountered. <code>req(FALSE)</code> terminates the reactive flow.</p>
<pre><code class="lang-r"><span class="hljs-comment"># show and hide modals  - Server</span>
location &lt;- reactive({
    query &lt;- input$location
    <span class="hljs-keyword">if</span>(openstreetmap_error_body(query, api_key) == <span class="hljs-string">"404"</span>){
      <span class="hljs-comment">#validate("No such city/town exists. Check your spelling!")</span>
      runjs(<span class="hljs-string">"$('#notFound').modal('show');"</span>)
      req(<span class="hljs-literal">FALSE</span>)
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(openstreetmap_error_body(query, api_key) == <span class="hljs-string">"400"</span>){
      <span class="hljs-comment">#validate("Bad request")</span>
      runjs(<span class="hljs-string">"$('#badRequest').modal('show');"</span>)
      req(<span class="hljs-literal">FALSE</span>)
    }
    coords &lt;- geocode(query, api_key)
  }) %&gt;% bindEvent(input$search)

<span class="hljs-comment"># listens for button click on modals to hide modal</span>
observeEvent(input$closeNotFound, {
    runjs(<span class="hljs-string">"$('#notFound').modal('hide');"</span>)
  })

observeEvent(input$closeBadRequest, {
    runjs(<span class="hljs-string">"$('#badRequest').modal('hide');"</span>)
  })
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you have built a weather app using Shiny that retrieves weather data from an API and displays it in an interactive and visually appealing way.</p>
<p>To do this, you used the following libraries:</p>
<ul>
<li><p><code>httr2</code> for making API requests and handling responses</p>
</li>
<li><p><code>shiny.semantic</code> for styling the app</p>
</li>
<li><p><code>lubridate</code> for working with and formatting time data</p>
</li>
<li><p><code>shinyjs</code> for integrating JavaScript features into the app</p>
</li>
</ul>
<p>This combination of tools allowed you to create a functional, user-friendly weather app.</p>
<p>You can find the complete code for the project <a target="_blank" href="https://github.com/elabongaatuo/R-weather-app">here</a>.</p>
<p>La Fin!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
