如何使用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) 并观察状态的边界:
接下来,您将附加 mouseover 和 mouseout 事件,以使用 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: '© <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 的 onEachFeature() 和 L.DomEvent.On 的交互性。
关于使用 Angular 和 Leaflet 的 4 部分系列到此结束。
如果您想了解有关 Angular 的更多信息,请查看 我们的 Angular 主题页面 以获取练习和编程项目。