Hands on

Collect Forecast Information with Angular and the HERE Weather API

By Nic Raboy | 24 September 2018

Knowing weather conditions for cities or coordinate based locations could be very important for your business. Imagine you’re operating a fleet of vehicles scheduled to perform activity at a certain location. If there are severe weather conditions, you know it can affect productivity or create safety concerns. Being able to gather forecast information programmatically opens the door to optimizing or automating different business scenarios.

We’re going to look at a simple scenario of getting the seven day forecast for a particular location using the HERE Weather API, REST, and Angular.

The Requirements

Because we’re using Angular and the HERE location services (HLS), there are a few requirements that must be met to be successful with this project:

  1. A HERE developer account must have been created.
  2. The Angular CLI must be installed and configured.

If you’re new to HERE, the good news is that you can create a new account for free through the HERE Developer Portal. In terms of the Angular CLI, it can be installed through the Node Package Manager (NPM).

So what are we going to be creating? Take a look at the image below:

angular-weather-api

As you can see in the above image, we are simply going to display the seven day forecast with icon illustrations for the weather, the temperature in Celsius as well as Fahrenheit, and the date that the weather reflects. There are plenty of things that can be discovered from the HERE Weather API, but our focus isn’t going to adventure elsewhere for this particular tutorial.

Starting a New Angular Project

For simplicity we are going to start by creating a fresh project to work with. Assuming that you have the Angular CLI ready to go, execute the following from the command line:

ng new here-project

With the project created, we need to install a separate dependency that will make our lives easier when working with date and time data. Navigate into the project and execute the following:

npm install moment --save

The above command will install Moment.js, which is a popular JavaScript library for parsing date and time data. We aren’t going to explore it until later in this tutorial.

Consuming Data from the HERE Weather API

Now that we have a project created we can start including the HERE Weather API so we can eventually display weather information to the user. In a production scenario it might make sense to create an Angular service for this, but since this is such a small example we can just include our HTTP logic directly in the component.

Before we can start collecting weather data, we need to configure our application for HTTP requests since it is disabled in Angular by default. Open the project’s src/app/app.module.ts file and include the following:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HttpClientJsonpModule } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule,
        HttpClientModule,
        HttpClientJsonpModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

You’ll notice that we’ve imported two modules. We’ve imported an HTTP client as well as the module that will allow us to make JSONP requests with the HTTP client. Because of cross-origin resource sharing (CORS), JSONP is a requirement. You can read more about CORS and the HERE Weather API in the official documentation.

With HTTP enabled, we can start developing the core logic as well as the UI to be rendered. Starting with the TypeScript logic, open the project’s src/app/app.component.ts file and include the following:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

    private appId: string;
    private appCode: string;

    public weather: any;

    public constructor(private http: HttpClient) {
        this.appId = "APP-ID-HERE";
        this.appCode = "APP-CODE-HERE";
        this.weather = [];
    }

    public ngOnInit() { }

    public getWeather(coordinates: any) {}

}

So what is happening in the above code?

First we’re importing a few classes and services. The HttpClient will allow us to make HTTP requests against the API and the map will allow us to transform our observable when making the request.

Within the AppComponent class we have two private variables which will represent our data from the HERE Developer Portal. The public weather variable will be populated from the API and rendered on the screen. These variables are all initialized in the constructor method and the HttpClient service is injected.

Now we can focus on the core of our logic.

Let’s look at the getWeather method first:

public getWeather(coordinates: any) {
    this.http.jsonp("https://weather.cit.api.here.com/weather/1.0/report.json?product=forecast_7days_simple&latitude=" + coordinates.latitude + "&longitude=" + coordinates.longitude + "&app_id=" + this.appId + "&app_code=" + this.appCode, "jsonpCallback")
        .pipe(map(result => (<any>result).dailyForecasts.forecastLocation))
        .subscribe(result => {
            this.weather = result.forecast;
        }, error => {
            console.error(error);
        });
}

In the above method we are making a JSONP request to the HERE Weather API. We are specifying that we want the forecast_7days_simple and we are providing our app id and app code. Most of the magic in this request comes in the latitude and longitude coordinates that are passed into the method. After executing the request, the result is transformed into something a little more pleasant and we subscribe to the result, at which point it is added to our public variable.

This is where things can get interesting. Instead of prompting the user to input an address or a latitude and longitude, we’re going to get that information from their web browser.

public ngOnInit() {
    if(navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(position => {
            this.getWeather(position.coords);
        });
    } else {
        console.error("The browser does not support geolocation...");
    }
}

In the ngOnInit Angular lifecycle event, we are checking the location using the HTML5 geolocation APIs. Assuming the browser is supported and the user grants permission, the coordinates will be passed into the getWeather method.

Let’s have a look at what the HTML that powers this application looks like. Open the project’s src/app/app.component.html file and include the following:

<div style="background-color: #F0F0F0; width: 75%; padding: 20px; border: 1px solid #CCC; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)">
    <p *ngIf="weather.length == 0">Searching...</p>
    <div *ngIf="weather.length > 0">
        <table style="width: 100%; text-align: center">
            <thead></thead>
            <tbody>
                <tr>
                    <td *ngFor="let forecast of weather">
                        <img [src]="forecast.iconLink" />
                        <p>{% raw %}{{ forecast.highTemperature }}{% endraw %}C</p>
                        <p>{% raw %}{{ forecast.utcTime }}{% endraw %}</p>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

In the above UI, we have two *ngIf conditions. If the weather variable does not yet have data, text saying that it is searching is shown. Once the weather variable has data, it is switched to a table where the weather data is looped and presented on the screen. The weather data returned from the HTTP request is a bunch of objects and we’re only looking at the icon graphic, the temperature in Celsius, and the UTC time.

The above HTML works, but we can do better!

Formatting Complex Data with Angular Pipes at Render

If you’re in the United States like I am, you probably have no idea how to read Celsius temperatures. At least, I have a difficult time doing it since it isn’t the norm around here. You probably also don’t necessarily want to see a long timestamp for the date as well. We can actually do data transformations at render time using Angular pipes. The plan is to convert Celsius to Fahrenheit and format the timestamp using Moment.js.

From the command line, execute the following:

ng g pipe fahrenheit
ng g pipe moment

The above two commands will create two different pipes for us. Starting with the time conversions, open the project’s src/app/moment.pipe.ts file and include the following:

import { Pipe, PipeTransform } from '@angular/core';
import * as moment from "moment";

@Pipe({
    name: 'moment'
})
export class MomentPipe implements PipeTransform {

    public transform(value: any, args?: any): any {
        return moment(value).format("MMMM DD, YYYY");
    }

}

What’s happening is when we decide to use the pipe, the value which is our timestamp will be formatted using Moment.js and returned. Similarly, open the project’s src/app/fahrenheit.pipe.ts file and include the following:

import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
    name: 'fahrenheit'
})
export class FahrenheitPipe implements PipeTransform {

    public transform(value: any, args?: any): any {
        return ((value * (9 / 5)) + 32).toFixed(2);
    }

}

In the above pipe, we are using the Celsius to Fahrenheit conversion formula to take the Celsius value and return it as Fahrenheit. Now that we have our pipes, we need to actually make use of them.

Open the project’s src/app/app.component.html file again because we need to make a change:

<div style="background-color: #F0F0F0; width: 75%; padding: 20px; border: 1px solid #CCC; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)">
    <p *ngIf="weather.length == 0">Searching...</p>
    <div *ngIf="weather.length > 0">
        <table style="width: 100%; text-align: center">
            <thead></thead>
            <tbody>
                <tr>
                    <td *ngFor="let forecast of weather">
                        <img [src]="forecast.iconLink" />
                        <p>{% raw %}{{ forecast.highTemperature }}{% endraw %}C / {% raw %}{{ forecast.highTemperature | fahrenheit }}{% endraw %}F</p>
                        <p>{% raw %}{{ forecast.utcTime | moment }}{% endraw %}</p>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
</div>

Inside our table we’ve made use of our two pipes. If you can’t see it, take a look at the following two lines:

<p>{% raw %}{{ forecast.highTemperature }}{% endraw %}C / {% raw %}{{ forecast.highTemperature | fahrenheit }}{% endraw %}F</p>
<p>{% raw %}{{ forecast.utcTime | moment }}{% endraw %}</p>

Notice that we’re using the pipe character and the name of the pipe that we created. The value on the left is passed into our transformation function and the result is rendered.

Conclusion

You just saw how to use the HERE Weather API in an Angular application that makes use of HTTP and JSONP requests. Going beyond what we saw in this tutorial, we could easily check for severe weather reports, sunrises and sunsets, and several other things that may be useful depending on the scenario.