リーフレットを使用してAngularでマップを作成する方法、パート4:シェイプサービス
序章
リーフレットは図形をサポートしています。 境界のデータを含むGeoJSONファイルを提供することにより、地図上に郡、州、国を示すことができます。
注:これは、AngularとLeafletの使用に関する4部構成のシリーズのパート4です。
このチュートリアルでは、アメリカ合衆国の大陸の州の形状をレンダリングする方法を学習します。
前提条件
このチュートリアルを完了するには、次のものが必要です。
- このチュートリアルは、前のパーツのインストールと手順に直接基づいています。
ステップ1—GeoJSONデータをダウンロードする
このチュートリアルでは、アメリカ合衆国の州の概要についてGeoJSONデータをプロットします。
Eric Celesteの米国のGeoJSONおよびKMLデータにアクセスし、5mのGeoJSONファイル(gz_2010_us_040_00_5m.json
)をダウンロードします。
このファイルを/assets/data
ディレクトリに保存します。
ステップ2—シェイプサービスを作成する
この時点で、AngularアプリケーションにLeafletの実装が機能しているはずです。
ターミナルウィンドウを使用して、プロジェクトディレクトリに移動します。 次に、次のコマンドを実行して、新しいサービスを生成します。
npx @angular/cli generate service shape --skip-tests
これにより、shape.service.ts
という新しいファイルが作成されます。
次に、この新しいサービスをプロバイダーとしてapp.module.ts
に追加します。
コードエディタで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 { PopupService } from './popup.service'; import { ShapeService } from './shape.service'; import { AppComponent } from './app.component'; import { MapComponent } from './map/map.component'; @NgModule({ declarations: [ AppComponent, MapComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [ MarkerService, PopupService, ShapeService ], bootstrap: [AppComponent] }) export class AppModule { }
これで、アプリケーションは新しいShapeService
をサポートします。
ステップ3—図形を読み込む
次に、新しく作成したshape.service.ts
をコードエディターで開き、HttpClient
をコンストラクターに追加します。
src / app / shape.service.ts
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class ShapeService { constructor(private http: HttpClient) { } getStateShapes() { return this.http.get('/assets/data/gz_2010_us_040_00_5m.json'); } }
関数getStateShapes()
は、シリアル化されたGeoJSONオブジェクトのオブザーバブルを返します。 これを使用するには、MapComponent
でオブザーバブルをサブスクライブする必要があります。
src / app / map / map.component.ts
import { Component, AfterViewInit } from '@angular/core'; import * as L from 'leaflet'; import { MarkerService } from '../marker.service'; import { ShapeService } from '../shape.service'; // ... @Component({ selector: 'app-map', templateUrl: './map.component.html', styleUrls: ['./map.component.css'] }) export class MapComponent implements AfterViewInit { private map; private states; constructor( private markerService: MarkerService, private shapeService: ShapeService ) { } // ... ngAfterViewInit(): void { this.initMap(); this.markerService.makeCapitalCircleMarkers(this.map); this.shapeService.getStateShapes().subscribe(states => { this.states = states; }); } }
このコードは、コンストラクターにShapeService
を挿入し、データを格納するローカル変数を作成し、getStateShapes()
関数を呼び出してデータをプルし、結果をサブスクライブします。
注:さらに良いアプローチは、リゾルバーにデータをプリロードすることです。
データが読み込まれたら、図形をレイヤーとしてマップに追加する必要があります。 Leafletは、活用できるGeoJSONレイヤー専用のファクトリを提供します。 このロジックを独自の関数に入れて、データが解決された後に呼び出しましょう。
src / app / map / map.component.ts
// ... @Component({ selector: 'app-map', templateUrl: './map.component.html', styleUrls: ['./map.component.css'] }) export class MapComponent implements AfterViewInit { private map; private states; // ... private initStatesLayer() { const stateLayer = L.geoJSON(this.states, { style: (feature) => ({ weight: 3, opacity: 0.5, color: '#008f68', fillOpacity: 0.8, fillColor: '#6DB65B' }) }); this.map.addLayer(stateLayer); } ngAfterViewInit(): void { this.initMap(); this.markerService.makeCapitalCircleMarkers(this.map); this.shapeService.getStateShapes().subscribe(states => { this.states = states; this.initStatesLayer(); }); } }
initStatesLayer()
関数は、新しいGeoJSONレイヤーを作成し、それをマップに追加します。
変更を保存します。 次に、アプリケーションを停止して再起動します。 Webブラウザ(localhost:4200
)でアプリケーションを開き、状態の境界線を確認します。
次に、mouseover
イベントとmouseout
イベントをアタッチして、onEachFeature
を使用して各図形を操作します。
src / app / map / map.component.ts
private highlightFeature(e) { const layer = e.target; layer.setStyle({ weight: 10, opacity: 1.0, color: '#DFA612', fillOpacity: 1.0, fillColor: '#FAE042' }); } private resetFeature(e) { const layer = e.target; layer.setStyle({ weight: 3, opacity: 0.5, color: '#008f68', fillOpacity: 0.8, fillColor: '#6DB65B' }); } private initStatesLayer() { const stateLayer = L.geoJSON(this.states, { style: (feature) => ({ weight: 3, opacity: 0.5, color: '#008f68', fillOpacity: 0.8, fillColor: '#6DB65B' }), onEachFeature: (feature, layer) => ( layer.on({ mouseover: (e) => (this.highlightFeature(e)), mouseout: (e) => (this.resetFeature(e)), }) ) }); this.map.addLayer(stateLayer); }
変更を保存します。 次に、Webブラウザ(localhost:4200
)でアプリケーションを開き、マウスを図形の上に移動します。
ただし、シェイプレイヤーがマーカーレイヤーの上にあるため、マーカーはかすかに見えます。
これに対処するには2つのアプローチがあります。 最初のアプローチは、makeCapitalCircleMarkers()
呼び出しをinitStatesLayer()
の直後に移動することです。 2番目のアプローチは、マップに追加された後、シェイプレイヤーでbringToBack()
を呼び出すことです。
bringToBack()
アプローチを使用した完全な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'; import { ShapeService } from '../shape.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; private states; constructor( private markerService: MarkerService, private shapeService: ShapeService ) { } 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: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' }); tiles.addTo(this.map); } private highlightFeature(e) { const layer = e.target; layer.setStyle({ weight: 10, opacity: 1.0, color: '#DFA612', fillOpacity: 1.0, fillColor: '#FAE042' }); } private resetFeature(e) { const layer = e.target; layer.setStyle({ weight: 3, opacity: 0.5, color: '#008f68', fillOpacity: 0.8, fillColor: '#6DB65B' }); } private initStatesLayer() { const stateLayer = L.geoJSON(this.states, { style: (feature) => ({ weight: 3, opacity: 0.5, color: '#008f68', fillOpacity: 0.8, fillColor: '#6DB65B' }), onEachFeature: (feature, layer) => ( layer.on({ mouseover: (e) => (this.highlightFeature(e)), mouseout: (e) => (this.resetFeature(e)), }) ) }); this.map.addLayer(stateLayer); stateLayer.bringToBack(); } ngAfterViewInit(): void { this.initMap(); // this.markerService.makeCapitalMarkers(this.map); this.markerService.makeCapitalCircleMarkers(this.map); this.shapeService.getStateShapes().subscribe(states => { this.states = states; this.initStatesLayer(); }); } }
変更を保存します。 次に、Webブラウザー(localhost:4200
)でアプリケーションを開き、州都の拡大縮小された円マーカーと州境の形状を観察します。
これで、形状をサポートするマップができました。
結論
この投稿では、データを読み込んで図形を作成する図形サービスを作成しました。 L.GeoJSON
のonEachFeature()
およびL.DomEvent.On
との双方向性を追加しました。
これで、AngularとLeafletの使用に関する4部構成のシリーズは終わりです。
Angularについて詳しく知りたい場合は、Angularトピックページで演習とプログラミングプロジェクトを確認してください。