如何使用Leaflet在Angular中构建地图,第2部分:标记服务
介绍
Leaflet 支持 标记。 这些是放置在地图上的指示器,可以包含信息。 这提供了一种在地图上突出显示地标和目的地的方法。
注意: 这是关于使用 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
以导入 Leaflet 并声明一个 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: '© <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); } }
如果此时您要运行应用程序,您会在控制台中遇到两个错误:
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)
您需要将 Leaflet 的资产导入您的项目以引用 marker-icon-2x.png
和 marker-shadow.png
图像文件。
打开 angular.json
文件并添加 Leaflet images
目录:
角.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" }
此代码将在本地复制 Leaflet 的标记图像。
然后,重新访问 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: '© <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
接受第三个可选参数。 此对象可以包含 radius
属性。 在 MarkerService
中,修改 makeCapitalCircleMarkers
函数以使用 20
的半径:
const circle = L.circleMarker([lat, lon], { radius: 20 }).addTo(map);
此代码将所有半径的大小设置为相同的值 (20
)。
接下来,您将更改半径以反映州府的人口:
static scaledRadius(val: number, maxVal: number): number { return 20 * (val / maxVal); }
该函数接受一个值(人口)、一个最大值(最大人口),并返回一个范围为 [0 - 20] 的半径。
您将使用扩展运算符和 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.marker
和 L.circleMarker
。 最后,您学习了如何通过传递半径函数来定义每个圆形标记的大小。
继续阅读本系列的 第 3 部分关于使用 Angular 和 Leaflet 。