UNPKG

@supermapgis/iclient-maplibregl

Version:

@supermapgis/iclient-maplibregl 是一套基于 Maplibre GL 的云 GIS 网络客户端开发平台, 支持访问 SuperMap iServer / iEdge / iPortal / iManager / Online 的地图、服务和资源,为用户提供了完整专业的 GIS 能力, 同时提供了优秀的可视化功能。

353 lines (328 loc) 12.4 kB
/* Copyright© 2000 - 2025 SuperMap Software Co.Ltd. All rights reserved. * This program are made available under the terms of the Apache License, Version 2.0 * which accompanies this distribution and is available at http://www.apache.org/licenses/LICENSE-2.0.html.*/ /** * reference and modification * dereklieu/cool-grid, cloudybay/leaflet.latlng-graticule * (https://github.com/dereklieu/cool-grid, https://github.com/cloudybay/leaflet.latlng-graticule) * Apache Licene 2.0 * thanks dereklieu, cloudybay */ import { Util as CommonUtil } from '@supermapgis/iclient-common/commontypes/Util'; import { GraticuleLayerRenderer } from '@supermapgis/iclient-common/overlay/graticule/GraticuleLayerRenderer'; import maplibregl from 'maplibre-gl'; /** * @class GraticuleLayer * @category Visualization GraticuleLayer * @classdesc 经纬网类。经纬网是由间隔均匀的经度线和纬度线组成的网络,用于在地图上识别各个位置的地理坐标。 * @version 11.1.0 * @modulecategory Overlay * @param {Object} options - 参数。 * @param {string} [options.layerID] - 图层 ID。默认使用 CommonUtil.createUniqueID("graticuleLayer_") 创建图层 ID。 * @param {boolean} [options.visible=true] - 是否显示经纬网。 * @param {boolean} [options.showLabel=true] - 是否显示标签。 * @param {number} [options.opacity=1] - 画布不透明度。 * @param {number|function} [options.interval = 10] - 经纬度的间隔(以度为单位),可以是数字,也可以是函数,参数是map。 * @param {maplibregl.LngLatBounds} [options.extent] - 经纬网渲染的边界范围([minx, miny, maxx, maxy]),不传为整个地图范围。 * @param {number} [options.minZoom] - 最小视图缩放级别(不包括此级别),在该级别之上,该层将可见。 * @param {number} [options.maxZoom] - 该图层可见的最大视图缩放级别(含)。 * @param {function} [options.lngLabelFormatter = null] - 经度标签转换函数。 * @param {function} [options.latLabelFormatter = null] - 纬度标签转换函数。 * @param {GraticuleLayer.LabelStyle} [options.lngLabelStyle] - 经度标签样式。 * @param {GraticuleLayer.LabelStyle} [options.latLabelStyle] - 纬度标签样式。 * @param {GraticuleLayer.StrokeStyle} [options.strokeStyle] - 绘制经纬线的样式。 * @usage */ /** * @typedef {Object} GraticuleLayer.LabelStyle - 标签样式。 * @property {Array.<string>} [textFont = ['Calibri','sans-serif']] - 字体样式。 * @property {string} [textSize = '12px'] - 字体大小。 * @property {string} [textColor ='rgba(0,0,0,1)'] - 字体颜色。 * @property {string} [textHaloColor ='rgba(255,255,255,1)'] - 描边颜色。 * @property {number} [textHaloWidth = 1] - 描边宽度。 * @property {string} [textAnchor = 'bottom'] - 字体基线: "center", "left", "right", "top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right"。 */ /** * @typedef {Object} GraticuleLayer.StrokeStyle - 线样式。 * @property {string} [lineColor = 'red'] - 线颜色。 * @property {string} [lineCap = 'round'] - 线端点风格:butt, round, square。 * @property {string} [lineJoin = round] - 线连接样式:bevel, round, miter。 * @property {Array.<number>} [lindDasharray = [0.5,4]] - 虚线样式。 * @property {number} [lineWidth = 1] - 线宽。 */ const defaultTextStyle = { textSize: '12px', textFont: ['12px Calibri', 'sans-serif'], textAnchor: 'bottom', textColor: 'rgba(0,0,0,1)', textHaloColor: 'rgba(255,255,255,1)', textHaloWidth: 1 }; const defaultStrokeStyle = { lineColor: 'red', lineCap: 'round', // butt, round, square lineJoin: 'round', // bevel, round, miter lindDasharray: [0.4, 5], // 数组|function lineDashOffset: 0, lineWidth: 1 // 数字|function }; const defaultOptions = { showLabel: true, opacity: 1, visible: true, interval: 10, // function|number extent: null, minZoom: 0, maxZoom: 50, wrapX: true, strokeStyle: defaultStrokeStyle, lngLabelFormatter: null, latLabelFormatter: null, lngLabelStyle: defaultTextStyle, latLabelStyle: defaultTextStyle }; export class GraticuleLayer { constructor(options) { this.id = options && options.layerID ? options.layerID : CommonUtil.createUniqueID('graticuleLayer_'); this.sourceId = this.id + '_line'; options = options || {}; options.strokeStyle = Object.assign({}, defaultStrokeStyle, options.strokeStyle || {}); options.lngLabelStyle = Object.assign({}, defaultTextStyle, options.lngLabelStyle || {}); options.latLabelStyle = Object.assign({}, defaultTextStyle, options.latLabelStyle || {}); this.options = Object.assign({}, defaultOptions, options); this.type = 'custom'; this.renderingMode = '3d'; this.overlay = true; this.styleDataEevent = this._setLayerTop.bind(this); } /** * @function GraticuleLayer.prototype.onAdd * @description 添加该图层。 * @param {maplibregl.Map} map - MapLibreGL Map 对象。 */ onAdd(map) { this.map = map; this.renderer = new GraticuleLayerRenderer(this.map, this.options, { getMapStateByKey: this.getMapStateByKey, getDefaultExtent: this.getDefaultExtent, updateGraticuleLayer: this.updateGraticuleLayer.bind(this), setVisibility: this.setVisibility.bind(this) }, { mapElement: this.map.getCanvas(), targetElement: this.map.getCanvasContainer(), id: this.id }); this.addGraticuleLayer(); this.resizeEvent = this.renderer._resizeCallback.bind(this.renderer); this.zoomendEvent = this.setVisibility.bind(this); this._bindEvent() } render() { this.renderer.draw(); } /** * @function GraticuleLayer.prototype.getMapStateByKey * @description 根据方法名与参数获取地图状态。 * @param {string} key - map 方法名。 * @param {Object} params - 参数。 */ getMapStateByKey(key, params) { if (key === 'getBearing') { return this.map.getBearing(); } else if (key === 'getBounds') { return this.map.getBounds(); } else if (key === 'project') { return this.map.project(params); } else if(key === 'unproject') { return this.map.unproject(params); } } /** * @function GraticuleLayer.prototype.onRemove * @description 移除图层回调。 */ onRemove() { this.renderer.onRemove(); this._unbindEvent(); } /** * @function GraticuleLayer.prototype.setVisibility * @description 设置可见性。 * @param {boolean} visible - 是否可见。 */ setVisibility(visible) { const zoom = this.map && this.map.getZoom(); this.options.visible = typeof visible === 'boolean' ? visible : this.options.visible; this.visible = typeof visible === 'boolean' ? visible : this.options.visible && zoom >= this.options.minZoom && zoom <= this.options.maxZoom; if (this.renderer) { this.renderer.visible = this.visible; } if (this.map.getLayer(this.sourceId)) { this.map.setLayoutProperty(this.sourceId, 'visibility', this.visible ? 'visible' : 'none'); } this.renderer && this.renderer._drawLabel(); } /** * @function GraticuleLayer.prototype.setMinZoom * @description 设置最小视图缩放级别。 * @param {number} minZoom - 最小视图缩放级别(不包括此级别),在该级别之上,该层将可见。 */ setMinZoom(minZoom) { this.options.minZoom = minZoom; this.setVisibility(); } /** * @function GraticuleLayer.prototype.setMaxZoom * @description 该图层可见的最大视图缩放级别。 * @param {number} maxZoom - 该图层可见的最大视图缩放级别(含)。 */ setMaxZoom(maxZoom) { this.options.maxZoom = maxZoom; this.setVisibility(); } /** * @function GraticuleLayer.prototype.setShowLabel * @description 设置显示标签。 * @param {boolean} showLabel - 是否显示标签。 */ setShowLabel(showLabel) { this.options.showLabel = showLabel; this.renderer._drawLabel(); } /** * @function GraticuleLayer.prototype.setExtent * @description 设置经纬网渲染的边界范围。 * @param {maplibregl.LngLatBounds} extent - 经纬网渲染的边界范围。 */ setExtent(extent) { this.options.extent = this.getDefaultExtent(extent, this.map); this.renderer.setExtent(extent); } /** * @function GraticuleLayer.prototype.setStrokeStyle * @description 设置经纬线样式。 * @param {GraticuleLayer.StrokeStyle} strokeStyle - 经纬线样式。 */ setStrokeStyle(strokeStyle) { if (!this.map || !this.map.getLayer(this.sourceId)) { return; } this.options.strokeStyle = strokeStyle; const { layout, paint } = this.renderer._transformStrokeStyle(strokeStyle); for (let key in layout) { this.map.setLayoutProperty(this.sourceId, key, layout[key]); } for (let key in paint) { this.map.setPaintProperty(this.sourceId, key, paint[key]); } } /** * @function GraticuleLayer.prototype.setLngLabelStyle * @description 设置经度标签样式。 * @param {GraticuleLayer.LabelStyle} labelStyle - 标签样式。 */ setLngLabelStyle(labelStyle) { this.options.lngLabelStyle = labelStyle; this.renderer._drawLabel(); } /** * @function GraticuleLayer.prototype.setLatLabelStyle * @description 设置纬度标签样式。 * @param {GraticuleLayer.LabelStyle} labelStyle - 标签样式。 */ setLatLabelStyle(labelStyle) { this.options.latLabelStyle = labelStyle; this.renderer._drawLabel(); } /** * @function GraticuleLayer.prototype.setIntervals * @description 设置经纬度的间隔(以度为单位)。 * @param {number|function} interval - 经纬度的间隔(以度为单位),可以是数字,也可以是函数,参数是map。 */ setIntervals(interval) { if (this.renderer) { this.renderer.setIntervals(interval); } } getDefaultExtent(extent, map = this.map) { const crs = (map.getCRS && map.getCRS()) || {}; let { lngLatExtent: crsExtent } = crs; if (!crsExtent) { crsExtent = [-180, -85.05119, 180, 85.05119]; } if (!extent || extent.length === 0) { return crsExtent; } const { _sw, _ne } = maplibregl.LngLatBounds.convert(extent); extent = [_sw.lng, _sw.lat, _ne.lng, _ne.lat]; extent = [ Math.max(crsExtent[0], extent[0]), Math.max(crsExtent[1], extent[1]), Math.min(crsExtent[2], extent[2]), Math.min(crsExtent[3], extent[3]) ]; return extent; } addGraticuleLayer() { if (!this.map.getSource(this.sourceId)) { const source = { type: 'geojson', data: { type: 'FeatureCollection', features: this.renderer.features } }; this.map.addSource(this.sourceId, source); } if (!this.map.getLayer(this.sourceId)) { const layer = Object.assign( { id: this.sourceId, type: 'line', source: this.sourceId }, this.renderer._transformStrokeStyle() ); this.map.addLayer(layer); } } updateGraticuleLayer(features = this.renderer.features) { if (this.map.getSource(this.sourceId)) { const geoJSONData = { type: 'FeatureCollection', features }; this.map.getSource(this.sourceId).setData(geoJSONData); } this.addGraticuleLayer(); } _bindEvent() { this.map.on('styledata', this.styleDataEevent); this.map.on('resize', this.resizeEvent); this.map.on('zoomend', this.zoomendEvent); } _unbindEvent() { this.map.off('styledata', this.styleDataEevent); this.map.off('resize', this.resizeEvent); this.map.off('zoomend', this.zoomendEvent); } _setLayerTop() { const map = this.map; if (!map) { return; } const layersOnMap = map.getStyle && map.getStyle().layers; if ( layersOnMap && layersOnMap.length && layersOnMap.findIndex(item => item.id === this.sourceId) !== layersOnMap.length - 1 ) { if (map.getLayer(this.sourceId)) { map.removeLayer(this.sourceId); this.addGraticuleLayer(); } } } _getLatPoints(lngRange, firstLng, lastLng, features) { return this.renderer._getLatPoints(lngRange, firstLng, lastLng, features); } }