如何使用 Leaflet 构建 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 对象的 observable。要使用它,您需要订阅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) 中打开应用程序并观察状态的边界:

带有各州形状的地图的屏幕截图。

接下来,您将附加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()在将形状图层添加到地图后调用它。

这是map.component.tsbringToBack()方法的完整文件

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.GeoJSON‘sonEachFeature()和 的交互性L.DomEvent.On

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

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

觉得文章有用?

点个广告表达一下你的爱意吧 !😁