Angularを使用してHERE Mapsを構築する
AngularにはWebアプリを構築するための強力なツールと機能のセットがあります。これらの機能を HERE SDK および API と組み合わせると、利便性がより高まります。
たとえば、HERE Maps を Angular と統合すると、インタラクティブ マップ、ジオコード アドレス、ルートの計算などを、Angular アプリのコンテキスト内で表示できます。
Angular アプリのコンテキスト内で簡単なインタラクティブ マップを作成する手順については、次のセクションを参照してください。
開始する前に
HERE Developer アカウントにサインアップし、API 資格情報を取得します。詳細については、「利用開始」を参照してください。
Angularプロジェクトを設定する
Angular CLIを使用して、新しいAngularアプリの開発を容易にします。
-
コマンド プロンプトで、次のコマンドを入力して Angular CLI をインストールします。
npm install -g @angular/cli -
次のコマンドを入力して、新しいAngularプロジェクトを開始します。
ng new jsapi-angular && cd jsapi-angular -
Which stylesheet format would you like to use?プロンプトで、CSSオプションを選択します。 -
Do you want to enable Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering)?プロンプトで、Nキーを押してサーバーサイドレンダリングと静的サイト生成プリレンダリングを無効にします。 -
Do you want to create a 'zoneless' application without zone.js?プロンプトで、yキーを押してzone.jsを含めないようにします。 -
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
-
プロジェクトディレクトリで、次のコマンドを使用してNPM設定にレジストリエントリーを追加して、https://repo.platform.here.com/でホストされている
maps-api-for-javascriptNPMパッケージをインストールします。npm config set @here:registry https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/ -
次のコマンドを入力して、
@here名前空間からパッケージをインストールします。npm install @here/maps-api-for-javascript -
jsapi-angularフォルダーに移動し、任意のテキスト エディターでtsconfig.jsonファイルを開きます。 -
tsconfig.jsonファイルで、angularCompilerOptionsの下に次の設定を追加します。"allowSyntheticDefaultImports": truetsconfig.jsonファイルのcompilerOptionsオブジェクト内に、次のオプションを追加します。"allowJs": true -
任意:セットアップが正常に完了したことを確認するには、次の操作を実行します。
-
次のコマンドを入力します。
ng serve結果:このコマンドは、「ホット リロード」機能を使用して開発サーバーを起動します。
-
Angular アプリを起動するには、ブラウザーでアドレス
http://localhost:4200/にアクセスします。結果:ブラウザーには
Congratulations! Your app is running.メッセージを含む、Angularのようこそ画面が表示されます。
-
静的マップコンポーネントを追加する
Angular アプリで、H.Map 名前空間インスタンスを含むコンポーネントを作成して静的マップを作成します。このコンポーネントは、対応するコンテナを通じてマップをレンダリングします。
-
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 ... -
jsapi-angular/src/app/jsmapディレクトリに移動して、jsmap.tsファイルを開きます。 -
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キーに置き換えます。
-
jsmap.htmlファイルを開いてファイルの内容を次のコードに置き換えます。<div #map id="map"></div> -
jsmap.cssファイルを開き、次のスタイルを追加します。#map { width: 100%; height: 500px; } -
jsapi-angular/src/appディレクトリで、app.htmlファイルを開いてファイルの内容を次のコードに置き換えます。<app-jsmap></app-jsmap> -
同じディレクトリで
app.tsファイルを開き、次の要素を追加します。-
Jsmapインポートステートメントを追加します。import { Jsmap } from './jsmap/jsmap'; -
imports配列で、RouterOutletをJsmapに置き換えます。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'); } -
-
ブラウザーでアドレス
http://localhost:4200/にアクセスして、マップが正しくレンダリングされていることを確認します。
結果:ブラウザーは、ベルリンを中心とし、ズームレベル13、高さ500ピクセル、幅はコンテナの100%を占めるビューポートに静的マップをレンダリングします。次の例のようになります。

マップのサイズを変更する
サイズが事前に設定された静的地図は、実行時に変更できません。
動的なサイズ変更を追加して、ユーザーがブラウザーウィンドウを拡大したときなどに、マップキャンバスがビューポートサイズの変化に反応して適応されるようにすると、静的マップを改善できます。
そのためには、マップにはコンテナの新しいサイズに合わせて調整するための明示的な resize() メソッド呼び出しが必要です。この例ではsimple-element-resize-detectorを使用して、Angularコンポーネント内でマップのサイズを変更する方法を紹介します。
-
Angular CLIで、作業ディレクトリが
jsapi-angularディレクトリであることを確認します。 -
次のコマンドを入力します。
npm install simple-element-resize-detector --save npm install @types/simple-element-resize-detector --save-dev -
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 -
次のコードスニペットに示すように、
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; } }
結果:コンポーネントは、外側ブラウザーウィンドウのサイズ変化に応じて動的に調整されます。
入力パラメーターに応答するようマップを設定する
別のコンポーネントを使用して入力を取得し、ズームレベルとマップの中心を変更します。
-
Angular CLIで次のコマンドを入力して、新しいコンポーネントを作成します。
ng generate component mapposition -
src/app/mappositionディレクトリに移動して、mapposition.htmlファイルを開きます。 -
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コンポーネントには、zoom、latitude、longitudeの3つの入力フィールドがあります。新しいコードでは各入力フィールドのイベントを親コンポーネントに再伝播するchangeイベントリスナーが使用されています。
-
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; } -
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クラスを使用して、ユーザー入力の変更を親コンポーネントに通知します。
-
src/appディレクトリでapp.htmlファイルを開き、ファイルの内容を次のコードに置き換えます。注<app-jsmap [zoom]="zoom" [lat]="lat" [lng]="lng" ></app-jsmap> <app-mapposition (notify)="handleInputChange($event)" ></app-mapposition>このコードでは親
appコンポーネントを使用して、マップフィールドと入力フィールド間で値を渡します。
-
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コンポーネントに渡します。 -
src/app/jsmapディレクトリでjsmap.tsファイルを開き、次の操作を実行します。-
次の例に示すように、最初のインポート ステートメントを変更して、
InputとSimpleChangesクラスを含めます。import { Component, ViewChild, ElementRef, Input, SimpleChanges } from '@angular/core'; -
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状態を更新します。
-
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; } } -
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> -
次の操作を実行して、
jsmap.tsファイルを更新します。-
jsmap.tsファイルで、次の例に示すようにOutputおよびEventEmitterクラスを追加して、インポートステートメントを変更します。import { Component, ViewChild, ElementRef, Input, SimpleChanges, Output, EventEmitter } from '@angular/core'; -
マップのインスタンス化後にインタラクティブな動作を可能にするリスナーを追加して、
ngAfterViewInitフックを更新します。map.addEventListener('mapviewchange', (ev: H.map.ChangeEvent) => { this.notify.emit(ev) }); new H.mapevents.Behavior(new H.mapevents.MapEvents(map)); -
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); }
-
-
mapposition.tsファイルで、マップの現在の位置を反映するようコンポーネントを更新します。- 次の図に示すように、
Inputから@angular/coreデコレーターをインポートします。import { Component, Output, EventEmitter, Input } from '@angular/core'; - クラス本体に次の入力パラメーターを追加します。
@Input() public zoom = 13; @Input() public lat = 52.5; @Input() public lng = 13.4;
- 次の図に示すように、
-
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コンポーネントであるJsmap、App、Mappositionの完全なコードサンプルが含まれています。
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リファレンス」を参照してください。
14 日前の更新