UNPKG

@ciniki/iclient-maplibregl

Version:

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

265 lines (258 loc) 9.41 kB
import maplibregl from 'maplibre-gl'; import { FetchRequest } from '@ciniki/iclient-common/util/FetchRequest'; import { MapService } from '../services/MapService'; import { InitMapServiceBase, isPlaneProjection, getZoom, getTileset, getTileFormat } from '@ciniki/iclient-common/iServer/InitMapServiceBase'; import { Util } from '@ciniki/iclient-common/commontypes/Util'; import { SecurityManager } from '@ciniki/iclient-common/security/SecurityManager'; import proj4 from 'proj4'; /** * @function initMap * @description 根据 SuperMap iServer 服务参数,创建地图与图层。目前仅支持 SuperMap iServer 地图服务。 * @category BaseTypes Util * @version 11.1.1 * @param {number} url - rest 地图服务地址。例如: http://{ip}:{port}/iserver/services/map-world/rest/maps/World。 * @param {Object} options - 参数。 * @param {Object} [options.type] - 地图类型。可选值 'raster' | 'vector-tile'。默认 'raster'。 * @param {Object} [options.mapOptions] - 地图配置,参数设置参考 {@link https://docs.mapbox.com/mapbox-gl-js/api/map/}。 * @param {string} [options.proxy] - 服务代理地址。 * @param {boolean} [options.withCredentials=false] - 请求是否携带 cookie。 * @param {boolean} [options.crossOrigin] - 是否允许跨域请求。 * @param {Object} [options.headers] - 请求头。 * @returns {Object} 实例对象。对象包括地图实例。 * @usage * ``` * // 浏览器 * <script type="text/javascript" src="{cdn}"></script> * <script> * const initMap = {namespace}.initMap(url, { mapOptions }); * * </script> * // ES6 Import * import { initMap } from '{npm}'; * * initMap(url, { mapOptions }) * ``` * */ export function initMap(url, options = {}) { const initMapService = new InitMapServiceBase(MapService, url, options); return initMapService.getMapInfo(async (res, resolve, reject) => { try { if (res.type === 'processCompleted') { const { dynamicProjection, prjCoordSys: { epsgCode, type } } = res.result; if (isPlaneProjection(type)) { reject(new Error('maplibre-gl cannot support plane coordinate system.')); return; } if (epsgCode !== 3857 && !dynamicProjection && !maplibregl.CRS) { reject( new Error( `The EPSG code ${epsgCode} needs to include maplibre-gl-enhance.js. Refer to the example: https://iclient.supermap.io/examples/maplibregl/editor.html#mvtVectorTile_2362` ) ); return; } const mapOptions = await createMapOptions(url, res.result, { ...options, initMapService }); const map = new maplibregl.Map(mapOptions); resolve({ map }); return; } reject(new Error('Fetch mapService is failed.')); } catch (error) { reject(error); } }); } /** * @private * @function getCrsExtent * @description 获取当前坐标系范围,[左,下,右,上]。 * @param {Object|Array} extent -坐标系范围。 * @returns {Array} */ function getCRSExtent(extent) { if (extent instanceof Array) { return extent; } if (extent.leftBottom && extent.rightTop) { return [extent.leftBottom.x, extent.leftBottom.y, extent.rightTop.x, extent.rightTop.y]; } return [extent.left, extent.bottom, extent.right, extent.top]; } /** * @private * @function defineCRSByWKT * @description 定义crs。 * @param {string} crsName - 投影名称。 * @param {string} wkt - wkt。 * @param {Object} extent - 坐标系范围。 * @returns {string} */ function defineCRSByWKT(crsName, wkt, extent) { const crsExtent = getCRSExtent(extent); const defineCRS = new maplibregl.CRS(crsName, wkt, crsExtent); return defineCRS; } /** * @private * @function transformMapCenter * @description 转换center。 * @param {Object} mapInfoCenter - 中心点。 * @param {string} baseProjection - 坐标投影。 * @returns {Array} */ function transformMapCenter(mapInfoCenter, sourceProjection) { let center = mapInfoCenter; if (sourceProjection === 'EPSG:3857') { return proj4(sourceProjection, 'EPSG:4326', mapInfoCenter); } if (sourceProjection !== 'EPSG:4326') { return maplibregl.proj4(sourceProjection, 'EPSG:4326', mapInfoCenter); } return center; } /** * @private * @function getVectorTileCRSExtent * @description 获取矢量瓦片坐标系范围。 * @param {string} vectorStyleUrl - 矢量瓦片 style json 服务地址。 * @param {string} restMapUrl - 矢量瓦片 rest 地图服务地址。 * @returns {Object} */ async function getVectorTileCRSExtent(vectorStyleUrl, restMapUrl) { try { const vectorStyleDataRes = await FetchRequest.get(vectorStyleUrl); const vectorStyleData = await vectorStyleDataRes.json(); if (vectorStyleData.metadata && vectorStyleData.metadata.indexbounds) { return { extent: vectorStyleData.metadata.indexbounds }; } const vectorExtentDataRes = await FetchRequest.get(`${restMapUrl}/prjCoordSys/projection/extent.json`); const vectorExtentData = await vectorExtentDataRes.json(); return { extent: vectorExtentData, center: vectorStyleData.center }; } catch (error) { return { extent: [] }; } } /** * @private * @function createMapOptions * @description 获取地图参数。 * @param {string} url - rest 地图服务地址。 * @param {Object} resetServiceInfo - rest 地图服务信息。 * @param {Object} [options] - 参数。 * @param {string} [options.type] - 服务代理地址。 * @param {Object} [options.mapOptions] - 地图配置。 * @param {Object} [options.initMapService] - InitMapServiceBase 实例。 * @returns {Object} mapParams。 */ async function createMapOptions(url, resetServiceInfo, options) { if (options.type && !['raster', 'vector-tile'].includes(options.type)) { return Promise.reject(new Error('type must be "raster" or "vector-tile".')); } const sourceType = options.type || 'raster'; const mapOptions = options.mapOptions || {}; const { prjCoordSys: { epsgCode }, bounds, center, dpi, coordUnit, scale } = resetServiceInfo; let mapCenter = center ? [center.x, center.y] : [0, 0]; let crs = `EPSG:${epsgCode}`; let extent = bounds; let tileUrl = sourceType === 'vector-tile' ? Util.urlAppend(Util.urlPathAppend(url, 'tileFeature/vectorstyles.json'), 'type=MapBox_GL&styleonly=true&tileURLTemplate=ZXY') : url; tileUrl = SecurityManager.appendCredential(tileUrl); let nonEnhanceExtraInfo = {}; let enhanceExtraInfo = {}; let zoom; let tileSize = 512; let tileFormat = 'png'; if (maplibregl.CRS) { const baseProjection = crs; const wkt = await options.initMapService.getWKT(); let vectorTileInfo; if (sourceType === 'vector-tile') { vectorTileInfo = await getVectorTileCRSExtent(tileUrl, url); extent = vectorTileInfo.extent; } crs = defineCRSByWKT(baseProjection, wkt, extent); if (sourceType === 'raster') { enhanceExtraInfo.rasterSource = 'iserver'; } if (vectorTileInfo && vectorTileInfo.center) { mapCenter = vectorTileInfo.center; } else { mapCenter = transformMapCenter(mapCenter, baseProjection); } const tilesets = await options.initMapService.getTilesets(); const tileset = getTileset(tilesets.result, { prjCoordSys: resetServiceInfo.prjCoordSys, tileType: 'Image' }); if (tileset) { tileFormat = getTileFormat(tileset); const maxWidth = Math.max(tileset.bounds.right - tileset.originalPoint.x, tileset.originalPoint.y - tileset.bounds.bottom); const tileCount = maxWidth / (tileset.resolutions[0] * 256); zoom = Math.ceil(Math.log2(tileCount)); const closestTileCount = Math.pow(2, zoom); const width = closestTileCount * 256 * tileset.resolutions[0]; const crsBounds = [ tileset.originalPoint.x, tileset.originalPoint.y - width, tileset.originalPoint.x + width, tileset.originalPoint.y ]; crs = new maplibregl.CRS(baseProjection, crsBounds); zoom = zoom - 1; tileSize = tileset.tileWidth; } } else { crs = 'EPSG:3857'; mapCenter = transformMapCenter(mapCenter, crs); if (sourceType === 'raster') { const tileSize = 256; nonEnhanceExtraInfo.tileSize = tileSize; const transparent = mapOptions.transparent !== false; tileUrl = Util.urlAppend(Util.urlPathAppend(tileUrl, 'zxyTileImage.png'), `z={z}&x={x}&y={y}&width=${tileSize}&height=${tileSize}&transparent=${transparent}`); } } if (zoom === undefined) { zoom = getZoom({ scale, dpi, coordUnit }, extent); } return { container: 'map', crs, center: mapCenter, zoom, style: sourceType === 'raster' ? { version: 8, sources: { 'smaples-source': { format: tileFormat, tileSize, type: 'raster', tiles: [tileUrl], ...nonEnhanceExtraInfo, ...enhanceExtraInfo } }, layers: [ { id: 'sample-layer', type: 'raster', source: 'smaples-source', minzoom: 0, maxzoom: 22 } ] } : tileUrl, ...mapOptions }; }