Assignment 10

Leaflet web map

Host a website on Github and build a web map with Leaflet to display real-time weather radar and alerts from the National Weather Service.

The web development concepts learned with the open-source Leaflet library can be applied to other web mapping libraries, such as the Google Maps JavaScript API and the ArcGIS JavaScript API. Github can be useful beyond web mapping for sharing source code and creating basic websites.

Table of Contents

Finished Example

Data

Aside from a basemap, we will have two layers to show current weather conditions in the United States:

Weather radar

Title: NEXRAD Base Reflectivity Current
Layer type: OGC Web Map Service
Summary: A National Weather Service image product depicting current precipitation derived from a network of radar stations. The web service is provided by Iowa State University Mesonet.
Service URL: https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi

Weather alerts

Title: National Weather Service Active Alerts
Layer type: GeoJSON
Summary: Areas under a weather alert issued by the National Weather Service, such as a tornado warning. See the NWS for details on the API.
Service URL: https://www.weather.gov/documentation/services-web-api#/default/get_alerts_active

Example Basemap

Title: OpenStreetMap
Layer type: Raster tiles
Summary: Crowd-sourced street map. See the details on the OpenStreetMap Wiki.
Service URL: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png

Steps

Part 1: Create a website

  1. Go to github.com/new and sign in to create a repository, which is like a project where you can put folders and files of source code.
    a. Name the repository yourusername.github.io, where yourusername is your Github username shown on the page.
    b. Select Public.
    c. Check Add a README file.
    d. Click Create repository.
    Github new repository
  2. On the Code tab for your new repository, click Add file > Create new file.
    Github new file
  3. Type weather/index.html and Github will automatically create a folder named weather containing a file named index.html. This will be the webpage people see first when they visit the website.
    Github file name
  4. Still on the Code tab, under Edit new file, paste the code below for a basic HTML skeleton of the homepage.
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title></title>
      </head>
      <body>
        The content goes here.
      </body>
    </html>
    
  5. We’re going to be making a weather map, so enter an appropriate title between the title tags. This will appear in browser tabs and search engine results, among other places.
    <title>Instructor's Weather Map</title>
    

    Webpage title
    The image illustrates where users will end up seeing your title in the browser. It is not part of the webpage content.

  6. Scroll down the page and click the green button, Commit new file. This saves the file. Go check out your page at: yourusername.github.io/weather. It should display “The content goes here.”
     

You now have a live website consisting of a single HTML file. In the next parts, we will add more HTML code to the file to prepare it for the web map, then create additional files for CSS and JavaScript.

Part 2: HTML setup

  1. Continue editing your page, weather/index.html. We will include two libraries (Leaflet and jQuery) to help build the map, and create an element to draw the map in. First, include the CSS file for Leaflet, which affects the appearance of the map. Under the title element, add these lines:
    <link href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" rel="stylesheet">
    <link href="weather.css" rel="stylesheet">
    

    This link element tells the user’s browser to download a CSS file from a URL. The first is the Leaflet CSS hosted by unpkg.com, and the other is a file we will create on Github soon called weather.css.

  2. Between the body tags, replace “The content goes here” with a div element, and give it an id attribute to identify it as the box where the map will eventually be drawn. For now, the page will be blank.
    <body>
      <div id="map"></div>
    </body>
    
  3. Then, under the div element but still within the body, add the lines:
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="weather.js"></script>
    

    These script tags tell the browser to download JavaScript files. The first two are libraries:

    • leaflet.js is the engine of our web map—it is the geographic information system that will understand geographic data and know how to draw it on a map.
    • jquery.js is a library with various shortcuts to make JavaScript easier to code; we will use it to fetch GeoJSON data from a URL.
    • weather.js will be our own custom script where we build the weather map.
  4. Commit changes. Go to your website address again to preview it, even though the page will still be blank. At the blank page, open your browser’s developer tools (F12 in Chrome). If you click on the Console tab, you should see errors saying that weather.css and weather.js cannot be found. That is because we haven’t created them yet.
    Error 404

Part 3: CSS setup

  1. From the editor page, click the weather folder:
    Github up folder
  2. Then from the weather folder page, click Add file > Create new file, and name it weather.css.
    Github file name
  3. For the code, paste these lines of CSS to make a full-page map then click Commit new file.
    body {
     padding: 0;
     margin: 0;
    }
    html, body, #map {
     height: 100%;
     width: 100%;
    }
    

The selector #map references our <div id="map"></div> element in the HTML file. The word used for the ID doesn’t have to be “map”, but it must match in both files.

These CSS styles tell the browser to draw the map at 100% of the browser window size with no spacing (padding, margin) around the edges, i.e., full page.

Part 4: JavaScript setup

  1. Back at the weather folder, create another new file and name it weather.js.
  2. Paste the following lines to create a map object and basemap. This will tell Leaflet to connect to the tile service at the specified URL, and the tiles will be assembled and displayed in the user’s browser in the div with the id of map:
    var map = L.map('map').setView([38, -95], 4);
    var basemapUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
    var basemap = L.tileLayer(basemapUrl).addTo(map);
    

    You should now have three files in your weather folder and you can preview your website to see if the basemap is working at yourusername.github.io/weather.
    Github files

    Leaflet basemap only

Now that the HTML, CSS, and JavaScript files are set up, we need to work on our JavaScript to add operational layers and popups to complete the weather map. We will not change the HTML and CSS any further.

Part 5: Radar layer

  1. Add the national precipitation radar layer using these lines in weather.js. We specify the URL of the web service, the layer name to use from the service, and format it as a transparent image to hide pixels with no precipitation.
    var radarUrl = 'https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi';
    var radarDisplayOptions = {
      layers: 'nexrad-n0r-900913',
      format: 'image/png',
      transparent: true
    };
    var radar = L.tileLayer.wms(radarUrl, radarDisplayOptions).addTo(map);
    

    If you preview the map, you should see the weather radar over the basemap, assuming there is precipitation somewhere in the country at the moment.
    Leaflet radar layer

Part 6: Alerts layer

Adding the weather alerts layer will involve more complex code, so let’s do it in small steps.

  1. Since we included the jQuery library in our HTML file, we can use the jQuery function getJSON to request current weather alerts from the National Weather Service in JSON format, saved to a variable named data. This function does not exist in plain JavaScript, and accomplishing this without jQuery would take some extra code.
    var weatherAlertsUrl = 'https://api.weather.gov/alerts/active?region_type=land';
    $.getJSON(weatherAlertsUrl, function(data) {
      // add more code here later
    });
    
  2. The code above gets the alert data but doesn’t display it. We need to pass the data variable to a Leaflet function to read the GeoJSON features and draw them on the map. Replace // add more code here later with:
    L.geoJSON(data).addTo(map);
    

    Now you can Commit changes and preview your map. You should see polygons (Leaflet’s default is blue) representing areas that have flood watches, thunderstorm warnings, etc.
    Leaflet alert polygons

  3. Let’s add a style to change the polygon color. Instead of only passing the data variable to the geoJSON function, we can pass options enclosed in curly brackets { }. Replace the previous line with the code below, which has been broken up onto multiple lines to make it easier to read:
    L.geoJSON(data, {
      style: { color: 'orange' },
      // more options go here
    }).addTo(map);
    

    The blue polygons will now be colored orange.
    Leaflet alert polygons

  4. In addition to the style option, we can add the onEachFeature option to tell Leaflet to call a function for every single feature in the layer. In our case, let’s attach a popup to each alert polygon to give details like “Flood Warning in Springfield MO”. Replace // more options go here with:
    onEachFeature: function(feature, layer) {
      layer.bindPopup(feature.properties.headline);
    }
    

    Leaflet popup
    Whatever text or variable is passed to the bindPopup function will be printed in the popup when the user clicks a polygon. If we put bindPopup('Hello'), then each popup would just show Hello. Instead, we are displaying the headline attribute that the National Weather Service includes in their alerts GeoJSON layer. You can view the NWS alerts web service URL in a browser to see the relevant attributes.
    NWS GeoJSON for the alerts services
    Due to the way Leaflet handles GeoJSON data, attribute values are accessed via the object feature.properties. You could show users the longer description instead of the headline by passing feature.properties.description to the bindPopup function.

  5. Finally, it would be nice to display the “severe” alerts in a different color to distinguish them from the watches and advisories. Revisit the style option: instead of specifying a color directly, we can write a function with some logic to determine the color based on an attribute value. This is the equivalent of changing the layer symbology in other GIS software. Replace style: { color: 'orange' }, with:
    style: function(feature){
      var alertColor = 'orange';
      if (feature.properties.severity === 'Severe') alertColor = 'red';
      return { color: alertColor };
    },
    

    That sets the color of alert polygons to orange, but if the alert has a severity of “Severe”, then the color for that polygon will be changed to red.
    Leaflet alert polygons colored red and orange
    Make sure your map works (yourusername.github.io/weather). If it doesn’t, press F12 to open your browser’s developer tools and look at the Console for errors that might hint at the problem. See the appendix for the full code example to compare for mistakes.

Try It

Go back and modify your existing map to meet the criteria below. Do not create a separate map for the Try It section.

  1. We already specified a color for “Severe” alerts. Add another line of code, in the right place, to make “Extreme” alerts a different color. If no extreme alerts are visible at the time, you could also add a line for “Minor” alerts, which are more common.
  2. Set a different basemap that works well with our weather layers. You can choose from the URLs below or venture out to the Leaflet Providers Demo for examples, though some of those might require signing up for a service.
     
    USGS Imagery Topo (Details)
    Service URL:
    https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryTopo/MapServer/tile/{z}/{y}/{x}
     
    Esri World Dark Gray (Details)
    Service URL:
    https://services.arcgisonline.com/arcgis/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}
     
  3. Edit your README.md file in your repository to link to the weather map with a short description, then Commit changes. The formatting used in README.md is called Markdown.
    • A single # starts a large heading
    • A double ## starts a subheading
    • Ending a line with a double space will begin a new line
    • Enclose a URL in < > to create a link, such as <https://yourusername.github.io/weather>
      Github README
      Your main repository page will then show the README with a link to the weather map.
      Github README

Checklist

  1. A working website hosted on Github
  2. A working map on the website
  3. A different basemap instead of OpenStreetMap
  4. Radar layer
  5. Alerts layer
  6. Symbology applied to alerts
  7. A new color applied to Extreme or Minor alerts
  8. Popup appears when an alert area is clicked, showing details
  9. README file on Github links to map

Submit

  1. A URL to your main repository page showing the README file with a link to your map. The URL you submit should look like https://github.com/yourusername/yourusername.github.io

Appendix: Full code

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Instructor's Weather Map</title>
    <link href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" rel="stylesheet">
    <link href="weather.css" rel="stylesheet">
  </head>
  <body>
    <div id="map"></div>
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script src="weather.js"></script>
  </body>
</html>

CSS

body {
  padding: 0;
  margin: 0;
}
html, body, #map {
  height: 100%;
  width: 100%;
}

JavaScript

// Create a new map centered on the continental US
var map = L.map('map').setView([38, -95], 4);

// Add OpenStreetMap to the map
var basemapUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var basemap = L.tileLayer(basemapUrl).addTo(map);

// Add weather radar to the map
var radarUrl = 'https://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi';
var radarDisplayOptions = {
  layers: 'nexrad-n0r-900913',
  format: 'image/png',
  transparent: true
};
var radar = L.tileLayer.wms(radarUrl, radarDisplayOptions).addTo(map);

// Get GeoJSON data from the NWS weather alerts API
var weatherAlertsUrl = 'https://api.weather.gov/alerts/active?region_type=land';
$.getJSON(weatherAlertsUrl, function(data) {
  L.geoJSON(data, {
    // Color all alert polygons orange, but color Severe polygons red
    style: function(feature){
      var alertColor = 'orange';
      if (feature.properties.severity === 'Severe') alertColor = 'red';
      return { color: alertColor }
    },
    // Add a popup on each feature showing the NWS alert headline
    onEachFeature: function(feature, layer) {
      layer.bindPopup(feature.properties.headline);
    }
  }).addTo(map);
});
Top