OpenLayers中map_为undefined报错,hover无法设置Overlay位置
问题描述
报错信息:ERROR TypeError: Cannot read properties of null (reading 'map_')(第29行),但第23行可正常遍历地图图层获取hover要素列表。组件依赖GeneralMapService管理地图实例与Overlay,需求是实现鼠标hover时设置Overlay的位置。
相关代码
Component代码
import { Component, OnInit } from '@angular/core'; import { Map } from 'ol'; import { GeneralMapService } from 'src/app/services/general-map.service'; @Component({ selector: 'app-application-map', templateUrl: './application-map.component.html', styleUrls: ['./application-map.component.css'] }) export class WhereisMapComponent implements OnInit { map_: Map; overlay: any; constructor(private generalMap: GeneralMapService) { this.map_ = this.generalMap.map this.map_.on("pointermove", (e) => { const positionOfMouse = e.coordinate this.map_.forEachFeatureAtPixel(e.pixel, function (feature) { if (feature) { const { as_tr_name, ass_track, id, descriptio, media } = feature.getProperties(); // 报错位置 console.log(this.map_.getView()) this.map_.getView().mapOverlay.setPosition(positionOfMouse); } }) }) this.overlay = document.getElementById('informationOverlay') this.generalMap.mapOverlay.set("id", "informationOverlay") this.generalMap.mapOverlay.setElement(this.overlay) } ngOnInit(): void { } ngAfterViewInit() { this.map_.setTarget("map") } }
General Service代码
import { Injectable } from '@angular/core'; import { Map, Overlay, Tile, View } from 'ol'; import TileLayer from 'ol/layer/Tile'; import OSM from 'ol/source/OSM'; import { Zoom, ZoomSlider, FullScreen } from 'ol/control'; import XYZ from 'ol/source/XYZ'; import BingMaps from 'ol/source/BingMaps'; import LayerGroup from 'ol/layer/Group'; import { mapDefaultCenter, mapeDefaultZoom } from '../shared/maptilerkey'; import { CreateLayerService } from './create-layer.service'; import { Fill, Stroke, Style } from 'ol/style'; import CircleStyle from 'ol/style/Circle'; import { OverlayService } from './overlay.service'; @Injectable({ providedIn: 'root' }) export class GeneralMapService { map: Map; zoomSlider: ZoomSlider = new ZoomSlider({ className: "zoom-slider-dashboard" }) mapLayers: LayerGroup = new LayerGroup({ layers: [] }) mapOverlay: Overlay; constructor(private createLayers: CreateLayerService, overlayService: OverlayService) { const topographicLayer = new TileLayer({ source: new XYZ({ url: "https://api.maptiler.com/maps/topographique/{z}/{x}/{y}@2x.png?key=WYPadsIvfisd0PUHFJ6K" }), visible: false }) topographicLayer.set('name', 'topographic'); const openStreetMap = new TileLayer({ source: new OSM(), visible: true }) openStreetMap.set('name', 'openstreetmap'); const bingMaps = new TileLayer({ preload: Infinity, source: new BingMaps({ key: 'AngczjEvgHNjwD8lTQe3DJ6CoFxavJfGTFCxxaGbS3bIgUW5_qn4k_m510RR53fe', imagerySet: "Aerial", hidpi: true, maxZoom: 19 }), visible: false }) bingMaps.set('name', 'bingmap'); this.map = new Map({ view: new View({ center: mapDefaultCenter, zoom: mapeDefaultZoom, minZoom: 5, projection: "EPSG:3857", extent: [15766342.542104144, -5590291.031415702, 16739198.402589425, -4713511.626202316] }), controls: [] }) const mapLayerGroup = new LayerGroup({ layers: [topographicLayer, openStreetMap, bingMaps] }) this.map.setLayerGroup(mapLayerGroup) this.map.addControl(this.zoomSlider) // vector layer const walkPointStyle = new Style({ image: new CircleStyle({ radius: 6, fill: new Fill({ color: '#44c4a1', }), stroke: new Stroke({ color: '#fff', width: 2, }), }), }) const walkPoints = this.createLayers.createVectorLayer("/assets/data/walking-data.geojson", walkPointStyle) const builtMapLayers = [ walkPoints ] this.map.getLayers().extend(builtMapLayers) // Overlay registration this.mapOverlay = overlayService.overlay this.map.addOverlay(this.mapOverlay) console.log("this.map", this.map.getOverlays()) } ngAfterContentInit() { } returnMap() { return this.map; } setMap(updatedMap: Map) { this.map = updatedMap; } }
修复建议
1. 修复回调函数的this指向问题
forEachFeatureAtPixel的回调使用普通function时,内部this会脱离组件上下文,导致this.map_为null。改用箭头函数绑定组件上下文:
this.map_.forEachFeatureAtPixel(e.pixel, (feature) => { if (feature) { const { as_tr_name, ass_track, id, descriptio, media } = feature.getProperties(); console.log(this.map_.getView()); // 修正Overlay访问路径 this.generalMap.mapOverlay.setPosition(positionOfMouse); } });
2. 修正Overlay的错误访问路径
原代码中this.map_.getView().mapOverlay是错误的,Overlay挂载在Map实例上,而非View。直接通过GeneralMapService的mapOverlay属性访问即可。
3. 调整DOM元素获取时机
构造函数执行时组件视图尚未初始化,document.getElementById('informationOverlay')会返回null,将这部分逻辑移到ngAfterViewInit钩子中:
ngAfterViewInit() { this.map_.setTarget("map"); this.overlay = document.getElementById('informationOverlay'); this.generalMap.mapOverlay.set("id", "informationOverlay"); this.generalMap.mapOverlay.setElement(this.overlay); }
4. 可选:添加空值防护
为避免潜在的空指针问题,可在关键位置添加条件判断:
if (feature && this.generalMap.mapOverlay) { // 执行业务逻辑 }
修改后的完整Component代码
import { Component, OnInit, AfterViewInit } from '@angular/core'; import { Map } from 'ol'; import { GeneralMapService } from 'src/app/services/general-map.service'; @Component({ selector: 'app-application-map', templateUrl: './application-map.component.html', styleUrls: ['./application-map.component.css'] }) export class WhereisMapComponent implements OnInit, AfterViewInit { map_: Map; overlay: any; constructor(private generalMap: GeneralMapService) { this.map_ = this.generalMap.map; this.map_.on("pointermove", (e) => { const positionOfMouse = e.coordinate; this.map_.forEachFeatureAtPixel(e.pixel, (feature) => { if (feature && this.generalMap.mapOverlay) { const { as_tr_name, ass_track, id, descriptio, media } = feature.getProperties(); console.log(this.map_.getView()); this.generalMap.mapOverlay.setPosition(positionOfMouse); } }); }); } ngOnInit(): void { } ngAfterViewInit(): void { this.map_.setTarget("map"); this.overlay = document.getElementById('informationOverlay'); if (this.overlay && this.generalMap.mapOverlay) { this.generalMap.mapOverlay.set("id", "informationOverlay"); this.generalMap.mapOverlay.setElement(this.overlay); } } }
内容的提问来源于stack exchange,提问作者Sarbraj Dulai
相关产品推荐
相关产品推荐

