リーフレットを使用してAngularでマップを作成する方法、パート2:マーカーサービス

提供:Dev Guides
移動先:案内検索

序章

リーフレットはマーカーをサポートします。 これらは、情報を含むことができるマップ上に配置されたインジケーターです。 これは、地図上のランドマークと目的地を強調表示する方法を提供します。

注:これは、AngularとLeafletの使用に関する4部構成のシリーズのパート2です。


このチュートリアルでは、サービスを使用してマーカーロジックを管理することにより、マップにマーカーを追加する方法を学習します。

前提条件

このチュートリアルを完了するには、次のものが必要です。

  • このチュートリアルは、前のパーツのインストールと手順に直接基づいています。

ステップ1—GeoJSONデータをダウンロードする

このチュートリアルでは、アメリカ合衆国の州都のGeoJSONデータをプロットします。 また、州名、首都名、人口に関する追加のメタデータも含まれます。

assetsディレクトリの下に新しいdataサブディレクトリを作成します。

mkdir src/assets/data

次に、usa-capitals.geojsonファイルをこのディレクトリに保存します。

ステップ2—マーカーサービスの作成

この時点で、AngularアプリケーションにLeafletの実装が機能しているはずです。

ターミナルウィンドウを使用して、プロジェクトディレクトリに移動します。 次に、次のコマンドを実行して、新しいサービスを生成します。

npx @angular/cli generate service marker --skip-tests

これにより、marker.service.tsという新しいファイルが作成されます。

次に、この新しいサービスをプロバイダーとしてapp.module.tsに追加します。 また、assetsフォルダーからデータをロードするため、HttpClientModuleを含める必要があります。

コードエディタでapp.module.tsを開き、次の変更を加えます。

src / app / app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { HttpClientModule } from '@angular/common/http';
import { MarkerService } from './marker.service';

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

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

これで、アプリケーションは新しいMarkerServiceをサポートします。

ステップ3—マーカーのロードとプロット

次に、新しく作成したmarker.service.tsをコードエディターで開き、HttpClientをコンストラクターに追加します。

src / app / marker.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class MarkerService {
  capitals: string = '/assets/data/usa-capitals.geojson';

  constructor(private http: HttpClient) { }
}

GeoJSONデータをロードし、マーカーを作成する新しい関数を作成します。 この関数は、リーフレットマップをパラメーターとして受け取ります。

marker.service.tsを変更してリーフレットをインポートし、makeCapitalMarkers関数を宣言します。

src / app / marker.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';

@Injectable({
  providedIn: 'root'
})
export class MarkerService {
  capitals: string = '/assets/data/usa-capitals.geojson';

  constructor(private http: HttpClient) { }

  makeCapitalMarkers(map: L.map): void { }
}

HttpClientを使用して、データとsubscribeを取得して結果を取得します。

データを取得したら、各フィーチャをループしてマーカーを作成し、それをマップに追加します。

src / app / marker.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';

@Injectable({
  providedIn: 'root'
})
export class MarkerService {
  capitals: string = '/assets/data/usa-capitals.geojson';

  constructor(private http: HttpClient) {
  }

  makeCapitalMarkers(map: L.map): void {
    this.http.get(this.capitals).subscribe((res: any) => {
      for (const c of res.features) {
        const lon = c.geometry.coordinates[0];
        const lat = c.geometry.coordinates[1];
        const marker = L.marker([lat, lon]);
        
        marker.addTo(map);
      }
    });
  }
}

このコードは、マーカーをマップにロードおよび追加するためのロジックを処理します。

ここで、MapComponentからこのメソッドを呼び出す必要があります。

src / app / map / map.component.ts

import { Component, AfterViewInit } from '@angular/core';
import * as L from 'leaflet';
import { MarkerService } from '../marker.service';

@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
  private map;

  private initMap(): void {
    this.map = L.map('map', {
      center: [ 39.8282, -98.5795 ],
      zoom: 3
    });

    const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      maxZoom: 18,
      minZoom: 3,
      attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    });

    tiles.addTo(this.map);
  }

  constructor(private markerService: MarkerService) { }

  ngAfterViewInit(): void {
    this.initMap();
    this.markerService.makeCapitalMarkers(this.map);
  }
}

この時点でアプリケーションを実行すると、コンソールで次の2つのエラーが発生します。

Outputmarker-icon-2x.png:1 GET http://localhost:4200/marker-icon-2x.png 404 (Not Found)
marker-shadow.png:1 GET http://localhost:4200/marker-shadow.png 404 (Not Found)

marker-icon-2x.pngおよびmarker-shadow.pngイメージファイルを参照するには、Leafletのアセットをプロジェクトにインポートする必要があります。

angular.jsonファイルを開き、リーフレットimagesディレクトリを追加します。

angle.json

{
  // ...
  "projects": {
    "angular-leaflet-example": {
      // ...
      "architect": {
        "build": {
          // ...
          "options": {
            // ...
            "assets": [
              "src/favicon.ico",
              "src/assets",
              {
                "glob": "**/*",
                "input": "node_modules/leaflet/dist/images/",
                "output": "./assets"
              }
            ],
            // ..
          },
          // ...
        },
        // ...
      }
    }},
  "defaultProject": "angular-leaflet-example"
}

このコードは、リーフレットのマーカー画像をローカルにコピーします。

次に、map.component.tsに再度アクセスして、アイコンを定義します。

src / app / map / map.component.ts

import { Component, AfterViewInit } from '@angular/core';
import * as L from 'leaflet';
import { MarkerService } from '../marker.service';

const iconRetinaUrl = 'assets/marker-icon-2x.png';
const iconUrl = 'assets/marker-icon.png';
const shadowUrl = 'assets/marker-shadow.png';
const iconDefault = L.icon({
  iconRetinaUrl,
  iconUrl,
  shadowUrl,
  iconSize: [25, 41],
  iconAnchor: [12, 41],
  popupAnchor: [1, -34],
  tooltipAnchor: [16, -28],
  shadowSize: [41, 41]
});
L.Marker.prototype.options.icon = iconDefault;

@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.css']
})
export class MapComponent implements AfterViewInit {
  private map;

  constructor(private markerService: MarkerService) { }

  private initMap(): void {
    this.map = L.map('map', {
      center: [ 39.8282, -98.5795 ],
      zoom: 3
    });

    const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      maxZoom: 18,
      minZoom: 3,
      attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    });

    tiles.addTo(this.map);
  }

  ngAfterViewInit(): void {
    this.initMap();
    this.markerService.makeCapitalMarkers(this.map);
  }
}

変更を保存します。 次に、アプリケーションを停止して再起動します。 Webブラウザー(localhost:4200)でアプリケーションを開き、州都のマーカーを確認します。

この時点で、デフォルトのマーカーをサポートするマップができました。

ステップ4—サークルマーカーの表示

この次のステップでは、マーカーをアイコンから円に変更します。 次に、州議会議事堂の人口を反映するように円のサイズをスケーリングします。

MarkerServiceを開き、makeCapitalCircleMarkers()関数を作成します。 makrCapitalMarkers()関数と非常によく似ています。 Leafletのmarkerメソッドの代わりに、circleMarkerメソッドを使用します。

src / app / marker.service.ts

makeCapitalCircleMarkers(map: L.map): void {
  this.http.get(this.capitals).subscribe((res: any) => {
    for (const c of res.features) {
      const lon = c.geometry.coordinates[0];
      const lat = c.geometry.coordinates[1];
      const circle = L.circleMarker([lat, lon]);
      
      circle.addTo(map);
    }
  });
}

次に、MapComponentでこの関数を呼び出します。

src / app / map / map.component.ts

ngAfterViewInit(): void {
  this.initMap();
  // this.markerService.makeCapitalMarkers(this.map);
  this.markerService.makeCapitalCircleMarkers(this.map);
}

これらの変更を保存し、Webブラウザ(localhost:4200)でアプリケーションを開きます。

アイコンが円に置き換えられました。

circleMarkerは、3番目のオプションのパラメーターを受け入れます。 このオブジェクトには、radiusプロパティを含めることができます。 MarkerServiceで、半径20を使用するようにmakeCapitalCircleMarkers関数を変更します。

const circle = L.circleMarker([lat, lon], { radius: 20 }).addTo(map);

このコードは、すべての半径を同じ値(20)になるようにサイズ設定します。

次に、州都の人口を反映するように半径を変更します。

static scaledRadius(val: number, maxVal: number): number {
  return 20 * (val / maxVal);
}

この関数は、値(母集団)、最大値(最大母集団)を受け取り、[0〜20]の範囲の半径を返します。

Spread-operatorとmapを使用して、人口が最も多い首都を見つけます。

const maxPop = Math.max(...res.features.map(x => x.properties.population), 0);

GeoJSONデータから、最大の人口は「アリゾナ州フェニックス」(1626078)になります。

最後に、半径関数としてScaledRadiusを使用して、すべてをまとめます。

コードエディタでMarkerServiceを開き、次の変更を加えます。

src / app / marker.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import * as L from 'leaflet';

@Injectable({
  providedIn: 'root'
})
export class MarkerService {
  capitals: string = '/assets/data/usa-capitals.geojson';

  constructor(private http: HttpClient) { }

  static scaledRadius(val: number, maxVal: number): number {
    return 20 * (val / maxVal);
  }

  makeCapitalMarkers(map: L.map): void {
    this.http.get(this.capitals).subscribe((res: any) => {
      for (const c of res.features) {
        const lon = c.geometry.coordinates[0];
        const lat = c.geometry.coordinates[1];
        const marker = L.marker([lat, lon]);
        
        marker.addTo(map);
      }
    });
  }

  makeCapitalCircleMarkers(map: L.map): void {
    this.http.get(this.capitals).subscribe((res: any) => {

      const maxPop = Math.max(...res.features.map(x => x.properties.population), 0);

      for (const c of res.features) {
        const lon = c.geometry.coordinates[0];
        const lat = c.geometry.coordinates[1];
        const circle = L.circleMarker([lat, lon], {
          radius: MarkerService.scaledRadius(c.properties.population, maxPop)
        });
        
        circle.addTo(map);
      }
    });
  }
}

変更を保存します。 次に、アプリケーションを停止して再起動します。 Webブラウザー(localhost:4200)でアプリケーションを開き、州都の新しいスケーリングされた円マーカーを観察します。

これで、マーカーをサポートするマップができました。

結論

この投稿では、データを読み込んでマーカーを作成するマーカーサービスを作成しました。 L.markerL.circleMarkerの2種類のマーカーを作成する方法を学びました。 最後に、半径の関数を渡すことにより、各円マーカーのサイズを定義する方法を学びました。

AngularとLeafletの使用に関するこのシリーズのパート3に進みます。