介绍
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 。