Working with Angular

Angular provides a powerful set of tools and features for building web applications. You can create an even more engaging experience for your users by combining these features with HERE SDKs and APIs.

For example, you can integrate HERE Maps with Angular to display interactive maps, geocode addresses, calculate routes, and more, all within the context of your Angular application.

See the following sections for step-by-step instructions on how to create a simple interactive map within the context of an Angular application:

Before you begin

Sign up for a HERE developer account and obtain your API credentials. For more information, see Get started.

Install Angular CLI

Facilitate the development of a new Angular application by using the Angular CLI.

  1. In the Command Prompt, enter the following command to install Angular CLI:
     npm install -g @angular/cli
    
  2. Initiate a new Angular project by entering the following command:
     ng new jsapi-angular && cd jsapi-angular
    
  3. Install Angular routing and use the CSS stylesheet by answering the corresponding prompts, as shown in the following example:

     ? Would you like to add Angular routing? Yes
     ? Which stylesheet format would you like to use? CSS
    

    Result: The system creates a new jsapi-angular directory, with the Angular components residing in the src sub-directory. The jsapi-angular directory has the following structure:

     jsapi-angular
     ├── node_modules
     ├── package.json
     ├── .gitignore
     ├── .browserslistrc
     ├── .editorconfig
     ├── angular.json
     ├── karma.conf.js
     ├── README.md
     ├── tsconfig.app.json
     ├── tsconfig.json
     ├── tsconfig.spec.json
     ├── dist
     │   ├── ...
     └── src
         ├── app
         │   ├── app-routing.module.ts
         │   ├── app.component.css
         │   ├── app.component.html
         │   ├── app.component.spec.ts
         │   ├── app.component.ts
         │   └── app.module.ts
         ├── assets
         │   ├── ...
         ├── environments
         |   ├── ...
         ├── favicon.ico
         ├── index.html
         ├── main.ts
         ├── polyfills.ts
         ├── styles.css
         └── test.ts
    
  4. Install the maps-api-for-javascript NPM package which is hosted at https://repo.platform.here.com/ by adding a registry entry to the NPM configuration through the following command:
     npm config set @here:registry https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/
    
  5. Install the package from the @here namespace by entering the following command:
     npm install @here/maps-api-for-javascript
    
  6. Navigate to the jsapi-angular folder, and then open the tsconfig.json file in a preferred text editor.

  7. In the tsconfig.json file, add the following setting under angularCompilerOptions:

     "allowSyntheticDefaultImports": true
    
  8. Optional: To verify that you completed the setup successfully, perform the following actions:

    1. Enter the following command:

       ng serve
      

      Result: This command launches a development server with the "hot reload" functionality.

    2. Initiate the Angular application by navigating to the http://localhost:4200/ address in the browser.

      Result: The browser displays the Angular welcome screen with the jsapi-angular is running! message.

Add a static map component

In your Angular application, generate a static map by creating a component that contains an H.Map namespace instance. This component renders the map through the corresponding container.

  1. In the Angular CLI, generate the jsmap component by entering the following command:
     ng generate component jsmap
    
    Result: The command creates a jsmap folder in the src/app directory. The folder contains all the files required to build the component:
     └── src
         ├── app
         │   ├── jsmap
         |   |   ├── jsmap.component.css
         |   |   ├── jsmap.component.html
         |   |   ├── jsmap.component.spec.ts
         |   |   └── jsmap.component.ts
         │   ├── app-routing.module.ts
         │   ├── app.component.css
         │   ├── app.component.html
         │   ├── app.component.spec.ts
         │   ├── app.component.ts
         │   └── app.module.ts
         ...
    
  2. Navigate to the jsapi-angular/src/app/jsmap directory, and then open the jsmap.component.ts file.

  3. In the jsmap.component.ts file, replace the default code with the following code:

     import { Component, ViewChild, ElementRef } from '@angular/core';
     import H from '@here/maps-api-for-javascript';
    
     @Component({
       selector: 'app-jsmap',
       templateUrl: './jsmap.component.html',
       styleUrls: ['./jsmap.component.css']
     })
     export class JsmapComponent {
    
       private map?: H.Map;
    
       @ViewChild('map') mapDiv?: ElementRef; 
    
       ngAfterViewInit(): void {
         if (!this.map && this.mapDiv) {
           // Instantiate a platform, default layers and a map as usual.
           const platform = new H.service.Platform({
             apikey: '{YOUR_API_KEY}'
           });
           const layers = platform.createDefaultLayers();
           const map = new H.Map(
             this.mapDiv.nativeElement,
             // Add type assertion to the layers object... 
             // ...to avoid any Type errors during compilation.
             (layers as any).vector.normal.map,
             {
               pixelRatio: window.devicePixelRatio,
               center: {lat: 0, lng: 0},
               zoom: 2,
             },
           );
           this.map = map;
         }
       }
     }
    

    This code imports the HERE Maps API for JavaScript library and instantiates the map through the ngAfterViewInit hook.

  4. Open the jsmap.component.html file, and then replace the file contents with the following code:

     <div #map id="map"></div>
    
  5. Open the jsmap.component.css file, and then add the following style:

     #map {
       width: 100%;
       height: 300px;
     }
    
  6. In the jsapi-angular/src/app directory, open the app.component.html file, and then replace the file content with following code:

     <app-jsmap></app-jsmap>
    
  7. Verify that the map renders correctly by navigating to the http://localhost:4200/ address in the browser.

Result: The browser renders a static map at the zoom level 2 and 0 latitude and longitude, in the viewport that is 300 pixels high and takes 100% of the enclosing container's width, similar to the following example: Static Map component with 100% width

Resize the map

A static map with a predetermined size cannot be changed during runtime, as indicated by the highlighted area in the following figure:

Static Map component
Figure 1. Static Map component

You can improve a static map by adding dynamic resizing to make the map canvas react and adapt to the changes in the viewport size, for example, when the user expands the browser window.

To achieve that, the map needs an explicit resize() method call to adjust to the new dimensions of the container.

This example uses simple-element-resize-detector to demonstrate how you can resize a map within an Angular component.

  1. In Angular CLI, switch your working directory to the jsapi-angular project directory.

  2. Enter the following commands :

     npm install simple-element-resize-detector --save
     npm install @types/simple-element-resize-detector --save-dev
    
  3. From the src/app/jsmap directory, open the jsmap.component.ts file, and then adjust the import statements by adding the simple-element-resize-detector library, as shown in the following example:

     import { Component, ViewChild, ElementRef } from '@angular/core';
     import H from '@here/maps-api-for-javascript';
     import onResize from 'simple-element-resize-detector';
    
  4. Update ngAfterViewInit method with the map.getViewPort().resize(), as shown in the following code snippet:

       ngAfterViewInit(): void {
         if (!this.map && this.mapDiv) {
           const platform = new H.service.Platform({
             apikey: '{YOUR_API_KEY}'
           });
           const layers = platform.createDefaultLayers();
           const map = new H.Map(
             this.mapDiv.nativeElement,
             (layers as any).vector.normal.map,
             {
               pixelRatio: window.devicePixelRatio,
               center: {lat: 0, lng: 0},
               zoom: 2,
             },
           );
           onResize(this.mapDiv.nativeElement, () => {
             map.getViewPort().resize();
           });
           this.map = map;
         }
       }
    

Result: The component adjusts dynamically to the changes in the size of the enclosing browser window, as shown in the following figure: Dynamic map component

Set the map to respond to input parameters

For a more complex use case, you can use another component to take your input to change the zoom level and the center of the map.

  1. Create a new component by entering the following command in Angular CLI:

     ng generate component mapposition
    
  2. In the jsap-angular project, navigate to the src/app/mapposition directory, and then open the mapposition.component.html file.

  3. Replace the default content of the mapposition.component.html file with the following code:

     <div>
       Zoom:
       <input
         (change)="notify.emit($event)"
         name="zoom"
         type="number"
         value="2"
       />
     </div>
     <div>
       Latitude:
       <input
         (change)="notify.emit($event)"
         name="lat"
         type="number"
         value="0"
       />
     </div>
     <div>
       Longitude:
       <input
         (change)="notify.emit($event)"
         name="lng"
         type="number"
         value="0"
       />
     </div>
    

    Note

    The mapposition component has three input fields: zoom, latitude and longitude. The new code introduces a change event listener that redispatches events for each input field to the parent component.

  4. From the src/app/mapposition/ directory, open the mapposition.component.ts fie, and then replace the file content with the following code:

     import { Component, Output, EventEmitter } from '@angular/core';
    
     @Component({
       selector: 'app-mapposition',
       templateUrl: './mapposition.component.html',
       styleUrls: ['./mapposition.component.css']
     })
     export class MappositionComponent {
    
       @Output() notify = new EventEmitter();
    
     }
    

    Note

    This TypeScript part of the component uses the @Output decorator and the EventEmitter class to notify the parent component about the changes in the user input.

  5. From the src/app directory, open the app.component.html file, and then replace the file content with the following code:

     <app-jsmap
       [zoom]="zoom"
       [lat]="lat"
       [lng]="lng"
     ></app-jsmap>
     <app-mapposition
       (notify)="handleInputChange($event)"
     ></app-mapposition>
    

    Note

    This code uses the parent app component to pass the values between the map and input fields.

  6. From the src/app directory, open the app.component.ts file, and then add a change handler by replacing the file content with the following code:

     import { Component } from '@angular/core';
    
     @Component({
       selector: 'app-root',
       templateUrl: './app.component.html',
       styleUrls: ['./app.component.css'],
     })
     export class AppComponent {
       title = 'jsapi-angular';
    
       constructor() {
         this.zoom = 2;
         this.lat = 0;
         this.lng = 0;
       }
    
       zoom: number;
       lat: number;
       lng: number;
    
       handleInputChange(event: Event) {
         const target = <HTMLInputElement> event.target;
         if (target) {
           if (target.name === 'zoom') {
             this.zoom = parseFloat(target.value);
           }
           if (target.name === 'lat') {
             this.lat = parseFloat(target.value);
           }
           if (target.name === 'lng') {
             this.lng = parseFloat(target.value);
           }
         }
       }
     }
    

    Result: The change handler updates the values according to the user's input and Angular passes these values to the jsmap component.

  7. From the src/app/jsmap directory, open the jsmap.component.ts file, and then perform the following actions:

    1. Adjust the first import statement to include Input and SimpleChanges classes, as shown in the following example:
       import { Component, ViewChild, ElementRef, Input, SimpleChanges } from '@angular/core';
      
    2. Within the JsmapComponent class, after the existing ngAfterViewInit hook definition, add @Input decorators for zoom, latitude, and longitude, and then add the ngOnChanges hook definition, as shown in the following code snippet:

       @Input() public zoom = 2;
       @Input() public lat = 0;
       @Input() public lng = 0;
      
       ngOnChanges(changes: SimpleChanges) {
         if (this.map) {
           if (changes['zoom'] !== undefined) {
             this.map.setZoom(changes['zoom'].currentValue);
           }
           if (changes['lat'] !== undefined) {
             this.map.setCenter({lat: changes['lat'].currentValue, lng: this.lng});
           }
           if (changes['lng'] !== undefined) {
             this.map.setCenter({lat: this.lat, lng: changes['lng'].currentValue});
           }
         }
       }
      

Result: Now, your Angular application can take your input with the help of the mapposition component, store the state in the app component, and then update the jsmap component so that the map responds to the input.

The following example demonstrates how the Angular application responds to the input by setting the coordinates to center the map on Berlin, and then increasing the zoom level: Map component that reacts to input

Enable dragging and zooming

Further increase the interactivity of your map by enabling map manipulation in the form of dragging or zooming in or out of the view.

To achieve this behaivor, add the mapviewchange listener to the H.Map instance, and then update the app state with the help of the EventEmitter.

  1. In the app.component.ts file, update the App state by adding the following code:

       handleMapChange(event: H.map.ChangeEvent) {
         if (event.newValue.lookAt) {
           const lookAt = event.newValue.lookAt;
           this.zoom = lookAt.zoom;
           this.lat = lookAt.position.lat;
           this.lng = lookAt.position.lng;
         }
       }
    
  2. In the app.component.html file, replace the current content with the following code:

     <app-jsmap
       [zoom]="zoom"
       [lat]="lat"
       [lng]="lng"
       (notify)="handleMapChange($event)"
     ></app-jsmap>
     <app-mapposition
       [zoom]="zoom"
       [lat]="lat"
       [lng]="lng"
       (notify)="handleInputChange($event)"
     ></app-mapposition>
    
  3. Update the jsmap.component.ts file by performing the following actions:

    1. In the jsmap.component.ts file, adjust the import statement by addin the Output and EventEmitter classes, as shown in the following example:

       import { Component, ViewChild, ElementRef, Input, SimpleChanges, Output, EventEmitter } from '@angular/core';
      
    2. Update the ngAfterViewInit hook by attaching the listener that enables the interactive behaviour after the map instantiation:

       map.addEventListener('mapviewchange', (ev: H.map.ChangeEvent) => {
         this.notify.emit(ev)
       });
       new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
      
    3. Update the ngOnChanges method to throttle the unnecessary map updates:

         private timeoutHandle: any;
         @Output() notify = new EventEmitter();
      
         ngOnChanges(changes: SimpleChanges) {
             clearTimeout(this.timeoutHandle);
             this.timeoutHandle = setTimeout(() => {
               if (this.map) {
                 if (changes['zoom'] !== undefined) {
                   this.map.setZoom(changes['zoom'].currentValue);
                 }
                 if (changes['lat'] !== undefined) {
                   this.map.setCenter({lat: changes['lat'].currentValue, lng: this.lng});
                 }
                 if (changes['lng'] !== undefined) {
                   this.map.setCenter({lat: this.lat, lng: changes['lng'].currentValue});
                 }
               }
             }, 100);
         }
      
  4. In the mapposition.component.ts file, update the comonent to reflect the current position of the map:

    1. Import the Input decorator from the @angular/core, as shown in the following example:
       import { Component, Output, EventEmitter, Input } from '@angular/core';
      
    2. Add the following input parameters must be added in the class body:
         @Input() public zoom = 2;
         @Input() public lat = 0;
         @Input() public lng = 0;
      
  5. In the mapposition.component.html file, adjust the template by replacing the file content with the following code:

     <div>
       Zoom:
       <input
         (change)="notify.emit($event)"
         name="zoom"
         type="number"
         [value]="zoom"
       />
     </div>
     <div>
       Latitude:
       <input
         (change)="notify.emit($event)"
         name="lat"
         type="number"
         [value]="lat"
       />
     </div>
     <div>
       Longitude:
       <input
         (change)="notify.emit($event)"
         name="lng"
         type="number"
         [value]="lng"
       />
     </div>
    

Result: The resulting application consist of the interactive map and input fields. Now, when you interact with the map, the application automatically updates the input fields, as shown in the following example: Interactive Map component

Add markers

The Angular application that you just created might serve as a template for adding further customizations or extensions that suit your business objectives. For example, you can add map markers to provide a quick and efficient way to visually represent the location of a point of interest on a map.

  1. In the jmaps.component.ts file, set the modify the map property to center on the area where you want to place the markers, as shown in the following example:

     const map = new H.Map(
       this.mapDiv.nativeElement,
       (layers as any).vector.normal.map,
       {
         pixelRatio: window.devicePixelRatio,
         // In this example, the map centers on
         // Luxembourg City, with the zoom level of 16:
         zoom: 16,
         center: { lat: 49.6107, lng: 6.1314 }
       },
     );
    
  2. Define a variable storing coordinates for each point of interest that you want to mark on the map, as shown in the following example:

     // This array stores coordinates of some interesting landmarks in Luxembourg City:
     const landmarks = [
       {name: 'Notre-Dame Cathedral', lat: 49.610364, lng: 6.129416,},
       {name: 'Grand Ducal Palace', lat: 49.611204, lng: 6.130720},
       {name: 'Casemates du Bock', lat: 49.611847, lng: 6.131925},
       {name: 'Adolphe Bridge', lat: 49.6083, lng: 6.127109}
     ];
    
  3. Create a marker for each landmark in the landmarks array:

     landmarks.forEach(landmark => {
       const marker = new H.map.Marker({ lat: landmark.lat, lng: landmark.lng });
       marker.setData(landmark.name);
       map.addObject(marker);
     });
    

    Note

    This code snippet sets each marker's data to the landmark name, and adds the marker to the map object using the addObject method.

Result: The map in your Angular application now centers on Luxembourg City and displays markers, as shown in the following figure: Map component that displys markers

Customize markers

You can control marker look and feel by providing custom icons for your markers. This example uses locally stored marker icons for simplicity.

  1. In the src/app/assets directory, create a new folder called images.

  2. For each landmark in the landmarks array, add a property called label, as shown in the following example:

     const landmarks = [
       {name: 'Notre-Dame Cathedral', lat: 49.610364, lng: 6.129416, label: 'NDC'},
       {name: 'Grand Ducal Palace',lat: 49.611204, lng: 6.130720, label: 'GDP'},
       {name: 'Casemates du Bock', lat: 49.611847, lng: 6.131925, label: 'CdB'},
       {name: 'Adolphe Bridge', lat: 49.6083, lng: 6.127109, label: 'AB'},
     ];
    
  3. For each landmark, create a custom icon that follows these naming conventions: marker-<label_value>.png, and then store the icon in the src/app/assets/images directory.

  4. Update the marker generation code to use custom icons:

     landmarks.forEach(landmark => {
       // For each marker, select the icon based on the corresponding landmark label:
       const icon = new H.map.Icon('/assets/images/marker-' + landmark.label + '.png',
         // Adjust the marker size to your needs: 
         {size: { w: 80, h: 80 }
           });
       const marker = new H.map.Marker({ lat: landmark.lat, lng: landmark.lng }, 
         { data: landmark.name, icon: icon });
         map.addObject(marker);
     });
    

Result: The map now displays custom markers, as shown in the following figure: Map object with custom markers

For more information on markers, see Marker Objects.

results matching ""

    No results matching ""