如何使用Leaflet在Angular中构建地图,第4部分:形状服务

来自菜鸟教程
跳转至:导航、​搜索

介绍

Leaflet 支持 形状 。 通过提供包含边界数据的 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 对象的 observable。 要使用它,您需要订阅 MapComponent 中的 observable。

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) 并观察状态的边界:

接下来,您将附加 mouseovermouseout 事件,以使用 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) 并将鼠标移到形状上:

但是,标记看起来很模糊,因为形状层位于标记层之上。

有两种方法可以解决这个问题。 第一种方法是将 makeCapitalCircleMarkers() 调用直接移到 initStatesLayer() 之后。 第二种方法是在形状图层添加到地图后调用 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: '&copy; <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.GeoJSONonEachFeature()L.DomEvent.On 的交互性。

关于使用 Angular 和 Leaflet 的 4 部分系列到此结束。

如果您想了解有关 Angular 的更多信息,请查看 我们的 Angular 主题页面 以获取练习和编程项目。