Карты в Angular и Leaflet: добавление форм
Development | Комментировать запись
Leaflet поддерживает формы. Предоставив программе файл GeoJSON с данными о границах формы, вы можете определять на картах страны, штаты, области и т.д.
Примечание: Это продолжение серии по использованию Angular и Leaflet. Другие части можно найти по тегу Leaflet maps.
В этом мануале вы узнаете, как добавить формы для штатов США.
Требования
Полный список требований вы найдете в первом мануале этой серии.
1: Загрузка данных GeoJSON
В этом руководстве мы будем использовать данные GeoJSON для отображения контуров штатов США.
Посетите эту ссылку и загрузите файл gz_2010_us_040_00_5m.json.
Сохраните этот файл в каталоге /assets/data.
2: Создание сервиса форм
На данный момент у вас должна быть рабочая реализация Leaflet в приложении Angular.
Используйте терминал, чтобы перейти в каталог проекта. Затем выполните следующую команду, чтобы создать новый сервис:
npx @angular/cli generate service shape --skip-tests
Эта команда создаст новый файл, shape.service.ts.
Затем добавьте этот новый сервис в свой файл app.module.ts в качестве провайдера.
Откройте 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 в конструктор:
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 и добавляет его на карту.
Сохраните эти изменения. Затем остановите приложение и перезапустите его. Откройте приложение в браузере (localhost:4200) и проверьте, правильно ли отображаются границы штатов.
Затем мы закрепим события mouseover и mouseout для взаимодействия с каждой из фигур с помощью onEachFeature:
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); }
Сохраните изменения. Затем откройте приложение в браузере (localhost:4200) и наведите указатель мыши на штат – он изменит свой цвет по границам.
Однако теперь метки выглядят тусклыми, потому что слой фигур находится над слоем меток.
Есть два подхода к решению этой проблемы. Первый подход – поместить вызов makeCapitalCircleMarkers() непосредственно после initStatesLayer(). Второй подход – вызвать метод bringToBack() на слой фигур после того, как он будет добавлен на карту.
Мы выбрали второй подход, метод bringToBack(). Вот полный файл 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();
});
}
}
Сохраните изменения. Затем откройте приложение в своем браузере (localhost:4200) и обратите внимание на круглые маркеры, которые мы создали во второй части этой серии – теперь они размещены над формами штатов.
Итак, вы создали карту, которая поддерживает фигуры.
Заключение
Этот мануал помог вам создать сервис форм, который загружает данные о границах и собирает фигуры. Мы добавили интерактивности с помощью методов onEachFeature() и L.DomEvent.On.
На этом мы заканчиваем нашу серию о создании карт на Angular и Leaflet.
Tags: Angular, AngularJS, Leaflet, Leaflet maps