ガイドv3.2 API Referencev3.1 API Reference
ガイド

Angularを使用してHERE Mapsを構築する

AngularにはWebアプリを構築するための強力なツールと機能のセットがあります。これらの機能を HERE SDK および API と組み合わせると、利便性がより高まります。

たとえば、HERE Maps を Angular と統合すると、インタラクティブ マップ、ジオコード アドレス、ルートの計算などを、Angular アプリのコンテキスト内で表示できます。

Angular アプリのコンテキスト内で簡単なインタラクティブ マップを作成する手順については、次のセクションを参照してください。

開始する前に

HERE Developer アカウントにサインアップし、API 資格情報を取得します。詳細については、「利用開始」を参照してください。

Angularプロジェクトを設定する

Angular CLIを使用して、新しいAngularアプリの開発を容易にします。

  1. コマンド プロンプトで、次のコマンドを入力して Angular CLI をインストールします。

    npm install -g @angular/cli
    
  2. 次のコマンドを入力して、新しいAngularプロジェクトを開始します。

    ng new jsapi-angular && cd jsapi-angular
    
  3. Which stylesheet format would you like to use?プロンプトで、CSSオプションを選択します。

  4. Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)?プロンプトで、Nキーを押してサーバーサイドレンダリングと静的サイト生成プリレンダリングを無効にします。

  5. Do you want to create a 'zoneless' application without zone.js?プロンプトで、yキーを押してzone.jsを含めないようにします。

  6. Which AI tools do you want to configure with Angular best practices?プロンプトで、Nキーを押してAIツールを設定しないようにします。

結果:必要なパッケージがインストールされ、jsapi-angularという新しいディレクトリが作成されます。Angularコンポーネントはsrcサブディレクトリ内に配置されます。jsapi-angularディレクトリの構造は次のとおりです。

jsapi-angular/
├── README.md
├── .editorconfig
├── .gitignore
├── angular.json
├── package-lock.json
├── package.json
├── public
│   └── favicon.ico
├── src
│   ├── app
│   │   ├── app.config.ts
│   │   ├── app.css
│   │   ├── app.html
│   │   ├── app.routes.ts
│   │   ├── app.spec.ts
│   │   └── app.ts
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── tsconfig.app.json
├── tsconfig.json
└── tsconfig.spec.json
  1. プロジェクトディレクトリで、次のコマンドを使用してNPM設定にレジストリエントリーを追加して、https://repo.platform.here.com/でホストされているmaps-api-for-javascript NPMパッケージをインストールします。

    npm config set @here:registry https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/
    
  2. 次のコマンドを入力して、@here名前空間からパッケージをインストールします。

    npm install @here/maps-api-for-javascript
    
  3. jsapi-angular フォルダーに移動し、任意のテキスト エディターで tsconfig.json ファイルを開きます。

  4. tsconfig.jsonファイルで、angularCompilerOptionsの下に次の設定を追加します。

    "allowSyntheticDefaultImports": true
    

    tsconfig.jsonファイルのcompilerOptionsオブジェクト内に、次のオプションを追加します。

    "allowJs": true
    
  5. 任意:セットアップが正常に完了したことを確認するには、次の操作を実行します。

    1. 次のコマンドを入力します。

      ng serve
      

      結果:このコマンドは、「ホット リロード」機能を使用して開発サーバーを起動します。

    2. Angular アプリを起動するには、ブラウザーでアドレス http://localhost:4200/ にアクセスします。

      結果:ブラウザーにはCongratulations! Your app is running.メッセージを含む、Angularのようこそ画面が表示されます。

Windowsオペレーティングシステムで「ng serve」プロセスを終了するには、「q + Enter」を押します。それでも終了しない場合は、Windowsコマンドシェルを開き、「netstat -ano | findstr 4200」でプロセスIDを特定し、「taskkill /PID /F」でプロセスを強制終了します。

静的マップコンポーネントを追加する

Angular アプリで、H.Map 名前空間インスタンスを含むコンポーネントを作成して静的マップを作成します。このコンポーネントは、対応するコンテナを通じてマップをレンダリングします。

  1. Angular CLI で次のコマンドを入力して jsmap コンポーネントを生成します。

    ng generate component jsmap
    

    結果:このコマンドにより src/app ディレクトリに jsmap フォルダーが作成されます。フォルダーには、コンポーネントの構築に必要なすべてのファイルが含まれています。

    └── src
        ├── app
        │   ├── app.config.ts
        │   ├── app.css
        │   ├── app.html
        │   ├── app.routes.ts
        │   ├── app.spec.ts
        │   ├── app.ts
        │   └── jsmap
        │       ├── jsmap.css
        │       ├── jsmap.html
        │       ├── jsmap.spec.ts
        │       └── jsmap.ts
        ...
    
  2. jsapi-angular/src/app/jsmapディレクトリに移動して、jsmap.tsファイルを開きます。

  3. jsmap.ts ファイルで、デフォルトのコードを次のコードに置き換えます。

    import { Component, ViewChild, ElementRef } from '@angular/core';
    import '@here/maps-api-for-javascript';
    
    @Component({
      selector: 'app-jsmap',
      imports: [],
      templateUrl: './jsmap.html',
      styleUrl: './jsmap.css'
    })
    export class Jsmap {
    
      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: 52.5, lng: 13.4},
              zoom: 13,
            },
          );
          this.map = map;
        }
      }
    }
    

    このコードではHERE Maps API for Javascriptライブラリをインポートし、ngAfterViewInitフックを通じてマップをインスタンス化します。

    {YOUR_API_KEY}を有効なHERE APIキーに置き換えます。

  4. jsmap.htmlファイルを開いてファイルの内容を次のコードに置き換えます。

    <div #map id="map"></div>
    
  5. jsmap.css ファイルを開き、次のスタイルを追加します。

    #map {
      width: 100%;
      height: 500px;
    }
    
  6. jsapi-angular/src/appディレクトリで、app.htmlファイルを開いてファイルの内容を次のコードに置き換えます。

    <app-jsmap></app-jsmap>
    
  7. 同じディレクトリでapp.tsファイルを開き、次の要素を追加します。

    • Jsmapインポートステートメントを追加します。

      import { Jsmap } from './jsmap/jsmap';
      
    • imports配列で、RouterOutletJsmapに置き換えます。

      imports: [Jsmap],
      

    次の更新されたapp.tsファイルを参照してください。

    import { Component, signal } from '@angular/core';
    import { Jsmap } from './jsmap/jsmap';
    @Component({
      selector: 'app-root',
      imports: [Jsmap],
      templateUrl: './app.html',
      styleUrl: './app.css'
    })
    export class App {
      protected readonly title = signal('jsapi-angular');
    }
    
  8. ブラウザーでアドレスhttp://localhost:4200/にアクセスして、マップが正しくレンダリングされていることを確認します。

結果:ブラウザーは、ベルリンを中心とし、ズームレベル13、高さ500ピクセル、幅はコンテナの100%を占めるビューポートに静的マップをレンダリングします。次の例のようになります。

HERE Maps API for Javascriptを使用してレンダリングされる基本的な静的マップコンポーネント

マップのサイズを変更する

サイズが事前に設定された静的地図は、実行時に変更できません。

動的なサイズ変更を追加して、ユーザーがブラウザーウィンドウを拡大したときなどに、マップキャンバスがビューポートサイズの変化に反応して適応されるようにすると、静的マップを改善できます。

そのためには、マップにはコンテナの新しいサイズに合わせて調整するための明示的な resize() メソッド呼び出しが必要です。この例ではsimple-element-resize-detectorを使用して、Angularコンポーネント内でマップのサイズを変更する方法を紹介します。

  1. Angular CLIで、作業ディレクトリがjsapi-angularディレクトリであることを確認します。

  2. 次のコマンドを入力します。

    npm install simple-element-resize-detector --save
    npm install @types/simple-element-resize-detector --save-dev
    
  3. src/app/jsmapディレクトリでjsmap.tsファイルを開き、次の例に示すようにsimple-element-resize-detectorライブラリを追加して、インポート宣言を変更します。

    import { Component, ViewChild, ElementRef } from '@angular/core';
    import '@here/maps-api-for-javascript';
    import onResize from 'simple-element-resize-detector'; // New import statement
    
  4. 次のコードスニペットに示すように、ngAfterViewInitメソッドをmap.getViewPort().resize()メソッド呼び出しで更新します。

      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: 52.5, lng: 13.4},
              zoom: 13,
            },
          );
          onResize(this.mapDiv.nativeElement, () => {
            map.getViewPort().resize();
          }); // Sets up the event listener to handle resizing
          this.map = map;
        }
      }
    

結果:コンポーネントは、外側ブラウザーウィンドウのサイズ変化に応じて動的に調整されます。

入力パラメーターに応答するようマップを設定する

別のコンポーネントを使用して入力を取得し、ズームレベルとマップの中心を変更します。

  1. Angular CLIで次のコマンドを入力して、新しいコンポーネントを作成します。

    ng generate component mapposition
    
  2. src/app/mappositionディレクトリに移動して、mapposition.htmlファイルを開きます。

  3. mapposition.htmlファイルのデフォルトの内容を次のコードに置き換えます。

    <div class="input-group">
      <label for="zoom">Zoom:</label>
      <input
        id="zoom"
        (change)="notify.emit($event)"
        name="zoom"
        type="number"
        value="13"
      />
    </div>
    <div class="input-group">
      <label for="lat">Latitude:</label>
      <input
        id="lat"
        (change)="notify.emit($event)"
        name="lat"
        type="number"
        value="52.5"
      />
    </div>
    <div class="input-group">
      <label for="lng">Longitude:</label>
      <input
        id="lng"
        (change)="notify.emit($event)"
        name="lng"
        type="number"
        value="13.4"
      />
    </div>
    

    mappositionコンポーネントには、zoomlatitudelongitudeの3つの入力フィールドがあります。新しいコードでは各入力フィールドのイベントを親コンポーネントに再伝播するchangeイベントリスナーが使用されています。

  4. src/app/mapposition/ディレクトリから、mapposition.cssファイルを開き、デフォルトの内容を次の基本スタイル設定に置き換えます。

    .input-group {
      margin-bottom: 5px;
      margin-top: 5px;
      display: flex;
      align-items: center;
    }
    
    label {
      width: 100px;
      font-weight: bold;
    }
    
    input[type="number"] {
      width: 100px;
      padding: 5px;
      margin-left: 10px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    
    input[type="number"]:focus {
      border-color: #007bff;
      outline: none;
    }
    
  5. src/app/mapposition/ディレクトリでmapposition.tsファイルを開き、ファイルの内容を次のコードに置き換えます。

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

    コンポーネントのこのTypeScript部分では、@OutputデコレーターとEventEmitterクラスを使用して、ユーザー入力の変更を親コンポーネントに通知します。

  6. src/appディレクトリでapp.htmlファイルを開き、ファイルの内容を次のコードに置き換えます。

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

    このコードでは親appコンポーネントを使用して、マップフィールドと入力フィールド間で値を渡します。

  7. src/appディレクトリでapp.tsファイルを開き、ファイルの内容を次のコードに置き換えて、変更ハンドラを追加します。

    import { Component } from '@angular/core';
    import { Jsmap } from './jsmap/jsmap';
    import { Mapposition } from './mapposition/mapposition'; // Imports Mapposition
    
    @Component({
      selector: 'app-root',
      imports: [Jsmap, Mapposition], // Adds Mapposition to the App template
      templateUrl: './app.html',
      styleUrl: './app.css'
    })
    export class App {
      title = 'jsapi-angular';
    
      // Initialized by the constructor, these properties store the current zoom level and coordinates (latitude and longitude) for the map
      constructor() {
        this.zoom = 13;
        this.lat = 52.5;
        this.lng = 13.4;
      }
    
      zoom: number;
      lat: number;
      lng: number;
    
      // Updates the zoom, lat, and lng properties based on user input.
      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);
          }
        }
      }
    }
    

    結果:変更ハンドラはユーザーの入力に従って値を更新し、Angular はこれらの値を jsmap コンポーネントに渡します。

  8. src/app/jsmap ディレクトリで jsmap.ts ファイルを開き、次の操作を実行します。

    1. 次の例に示すように、最初のインポート ステートメントを変更して、InputSimpleChanges クラスを含めます。

      import { Component, ViewChild, ElementRef, Input, SimpleChanges } from '@angular/core';
      
    2. Jsmap クラス内で、既存の ngAfterViewInit フック定義の後にズーム、緯度、経度用の @Input デコレーターを追加し、ngOnChanges フック定義を追加します (以下のコード スニペットを参照)。

      @Input() public zoom = 13;
      @Input() public lat = 52.5;
      @Input() public lng = 13.4;
      
      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});
          }
        }
      }
      

結果:これで、Angularアプリは、mappositionコンポーネントを使用して入力を受け取り、状態をappコンポーネントに保存し、マップが入力に応答するようjsmapコンポーネントを更新できるようになります。

ドラッグとズームを有効にする

ビューをドラッグ、ズームイン、ズームアウトする形式でマップ操作を有効にすると、マップの双方向性がさらに向上します。

この動作を実現するには、mapviewchangeリスナーをH.Mapインスタンスに追加し、EventEmitterを使用してapp状態を更新します。

  1. app.tsファイルで、次のコードを追加してApp状態を更新します。

      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. app.html ファイルで、現在の内容を次のコードに置き換えます。

    <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. 次の操作を実行して、jsmap.tsファイルを更新します。

    1. jsmap.tsファイルで、次の例に示すようにOutputおよびEventEmitterクラスを追加して、インポートステートメントを変更します。

      import { Component, ViewChild, ElementRef, Input, SimpleChanges, Output, EventEmitter } from '@angular/core';
      
    2. マップのインスタンス化後にインタラクティブな動作を可能にするリスナーを追加して、ngAfterViewInitフックを更新します。

      map.addEventListener('mapviewchange', (ev: H.map.ChangeEvent) => {
        this.notify.emit(ev)
      });
      new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
      
    3. ngOnChangesメソッドを更新して、不要なマップ更新を抑制します。

        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. mapposition.tsファイルで、マップの現在の位置を反映するようコンポーネントを更新します。

    1. 次の図に示すように、Inputから@angular/coreデコレーターをインポートします。
      import { Component, Output, EventEmitter, Input } from '@angular/core';
      
    2. クラス本体に次の入力パラメーターを追加します。
        @Input() public zoom = 13;
        @Input() public lat = 52.5;
        @Input() public lng = 13.4;
      
  5. mapposition.htmlファイルで、ファイルの内容を次のコードに置き換えて、テンプレートを調整します。

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

結果:結果として得られるアプリは、インタラクティブマップと入力フィールドで構成されます。マップを操作すると、次の例に示すように、アプリケーションは入力フィールドを自動的に更新します。

インタラクティブマップコンポーネント

コードサンプル

次のセクションには、このチュートリアルで使用する主なAngularコンポーネントであるJsmapAppMappositionの完全なコードサンプルが含まれています。

Jsmapコンポーネント

jsmap.tsのソースコードを表示します。

import { Component, ViewChild, ElementRef, Input, SimpleChanges, Output, EventEmitter } from '@angular/core';
import '@here/maps-api-for-javascript';
import onResize from 'simple-element-resize-detector';

@Component({
    selector: 'app-jsmap',
    imports: [],
    templateUrl: './jsmap.html',
    styleUrl: './jsmap.css'
})
export class Jsmap {

  private map?: H.Map;

  @ViewChild('map') mapDiv?: ElementRef;

    ngAfterViewInit(): void {
    if (!this.map && this.mapDiv) {
      const platform = new H.service.Platform({
        apikey: 'YOUR_HERE_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: 52.53074, lng: 13.38492},
          zoom: 19,
        },
      );
      onResize(this.mapDiv.nativeElement, () => {
        map.getViewPort().resize();
      });
      this.map = map;

      map.addEventListener('mapviewchange', (ev: H.map.ChangeEvent) => {
        this.notify.emit(ev)
      });
      new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
    }
  }

  @Input() public zoom = 13;
  @Input() public lat = 52.5;
  @Input() public lng = 13.4;

  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);
  }
}

上記のコードサンプル内のYOUR_HERE_API_KEYは、有効なAPIキーに置き換えてください。

Appコンポーネント

app.tsのソースコードを表示します。

import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { JsmapComponent } from './jsmap/jsmap.component';
import { MappositionComponent } from './mapposition/mapposition.component';

@Component({
  selector: 'app-root',
  imports: [Jsmap, Mapposition], // Adds Mapposition to the App template
  templateUrl: './app.html',
  styleUrl: './app.css'
  })
export class App {
  title = 'jsapi-angular';

  constructor() {
    this.zoom = 13;
    this.lat = 52.5;
    this.lng = 13.4;
  }

  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);
      }
    }
  }

  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;
    }
  }
}

Mappositionコンポーネント

mapposition.tsのソースコードを表示します。

import { Component, Output, EventEmitter, Input } from '@angular/core';

@Component({
  selector: 'app-mapposition',
  imports: [],
  templateUrl: './mapposition.html',
  styleUrl: './mapposition.css'
})
export class Mapposition {

  @Output() notify = new EventEmitter();

  @Input() public zoom = 13;
  @Input() public lat = 52.5;
  @Input() public lng = 13.4;

}

次のステップ

HERE Maps API for Javascriptの設計とフィーチャーの詳細については、「APIリファレンス」を参照してください。