如何使用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: '&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);
  }
}

如果此时您要运行应用程序,您会在控制台中遇到两个错误:

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.pngmarker-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: '&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 接受第三个可选参数。 此对象可以包含 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.markerL.circleMarker。 最后,您学习了如何通过传递半径函数来定义每个圆形标记的大小。

继续阅读本系列的 第 3 部分关于使用 Angular 和 Leaflet