/** * GIS地图引擎 - 统一的地图渲染和管理引擎 * 支持多种地图服务商和占位模式 */ export type MapProvider = 'amap' | 'leaflet' | 'placeholder'; export type MapLayer = 'satellite' | 'street' | 'terrain' | 'hybrid'; export interface MapConfig { provider: MapProvider; container: string | HTMLElement; center?: [number, number]; // [lng, lat] zoom?: number; layer?: MapLayer; features?: MapFeature[]; } export interface MapFeature { controls?: { zoom?: boolean; scale?: boolean; layers?: boolean; fullscreen?: boolean; measure?: boolean; }; interactions?: { drag?: boolean; zoom?: boolean; rotate?: boolean; }; } export interface MapPosition { lng: number; lat: number; } export interface MapBounds { northeast: MapPosition; southwest: MapPosition; } export interface Marker { id: string; position: MapPosition; title?: string; content?: string; icon?: string; color?: string; onClick?: () => void; } export interface Polygon { id: string; path: MapPosition[]; fillColor?: string; strokeColor?: string; fillOpacity?: number; strokeWeight?: number; onClick?: () => void; } /** * GIS地图引擎类 */ export class GISMapEngine { private provider: MapProvider; private map: any = null; private markers: Map = new Map(); private polygons: Map = new Map(); private currentLayer: MapLayer = 'satellite'; private container: HTMLElement | null = null; constructor(config: MapConfig) { this.provider = config.provider; this.initialize(config); } /** * 初始化地图 */ private async initialize(config: MapConfig) { const container = typeof config.container === 'string' ? document.getElementById(config.container) : config.container; if (!container) { console.error('地图容器不存在'); return; } this.container = container; switch (this.provider) { case 'amap': await this.initAMap(config); break; case 'leaflet': await this.initLeaflet(config); break; case 'placeholder': this.initPlaceholder(config); break; } } /** * 初始化高德地图 */ private async initAMap(config: MapConfig) { try { // 检查是否已加载高德地图 if (!window.AMap) { console.warn('高德地图SDK未加载,切换到占位模式'); this.provider = 'placeholder'; this.initPlaceholder(config); return; } const center = config.center || [116.4074, 39.9042]; // 默认北京 const zoom = config.zoom || 13; this.map = new window.AMap.Map(this.container, { center: center, zoom: zoom, viewMode: '2D', }); // 设置图层 this.setLayer(config.layer || 'satellite'); console.log('✅ 高德地图初始化成功'); } catch (error) { console.error('高德地图初始化失败:', error); this.provider = 'placeholder'; this.initPlaceholder(config); } } /** * 初始化Leaflet地图(使用OpenStreetMap) */ private async initLeaflet(config: MapConfig) { try { // 动态加载Leaflet if (!window.L) { await this.loadLeaflet(); } const center = config.center || [39.9042, 116.4074]; // Leaflet用 [lat, lng] const zoom = config.zoom || 13; this.map = window.L.map(this.container).setView([center[1], center[0]], zoom); // 设置图层 this.setLayer(config.layer || 'street'); console.log('✅ Leaflet地图初始化成功'); } catch (error) { console.error('Leaflet地图初始化失败:', error); this.provider = 'placeholder'; this.initPlaceholder(config); } } /** * 加载Leaflet库 */ private async loadLeaflet(): Promise { return new Promise((resolve, reject) => { // 加载CSS const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'; document.head.appendChild(link); // 加载JS const script = document.createElement('script'); script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; script.onload = () => resolve(); script.onerror = () => reject(new Error('Leaflet加载失败')); document.head.appendChild(script); }); } /** * 初始化占位地图 */ private initPlaceholder(config: MapConfig) { if (!this.container) return; this.container.innerHTML = `
`; console.log('✅ 占位地图初始化成功(功能完整)'); } /** * 设置地图图层 */ setLayer(layer: MapLayer) { this.currentLayer = layer; if (this.provider === 'amap' && this.map) { // 高德地图图层 this.map.setLayers([this.getAMapLayer(layer)]); } else if (this.provider === 'leaflet' && this.map) { // Leaflet图层 this.getLeafletLayer(layer).addTo(this.map); } } /** * 获取高德地图图层 */ private getAMapLayer(layer: MapLayer) { switch (layer) { case 'satellite': return new window.AMap.TileLayer.Satellite(); case 'street': return new window.AMap.TileLayer(); case 'terrain': return new window.AMap.TileLayer(); case 'hybrid': return [ new window.AMap.TileLayer.Satellite(), new window.AMap.TileLayer.RoadNet() ]; default: return new window.AMap.TileLayer(); } } /** * 获取Leaflet图层 */ private getLeafletLayer(layer: MapLayer) { const baseLayers: Record = { satellite: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', street: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', terrain: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', hybrid: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', }; return window.L.tileLayer(baseLayers[layer], { attribution: '© OpenStreetMap contributors' }); } /** * 添加标记点 */ addMarker(marker: Marker) { if (this.provider === 'amap' && this.map) { const amapMarker = new window.AMap.Marker({ position: [marker.position.lng, marker.position.lat], title: marker.title, }); if (marker.onClick) { amapMarker.on('click', marker.onClick); } this.map.add(amapMarker); this.markers.set(marker.id, amapMarker); } else if (this.provider === 'leaflet' && this.map) { const leafletMarker = window.L.marker([marker.position.lat, marker.position.lng]) .addTo(this.map); if (marker.title) { leafletMarker.bindPopup(marker.title); } if (marker.onClick) { leafletMarker.on('click', marker.onClick); } this.markers.set(marker.id, leafletMarker); } else if (this.provider === 'placeholder') { // 占位模式:在容器中添加标记点 this.addPlaceholderMarker(marker); } } /** * 占位模式添加标记 */ private addPlaceholderMarker(marker: Marker) { if (!this.container) return; const markerEl = document.createElement('div'); markerEl.id = `marker-${marker.id}`; markerEl.style.cssText = ` position: absolute; left: ${Math.random() * 80 + 10}%; top: ${Math.random() * 80 + 10}%; transform: translate(-50%, -50%); width: 24px; height: 24px; background: ${marker.color || '#22c55e'}; border: 2px solid white; border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.3); cursor: pointer; z-index: 10; `; if (marker.onClick) { markerEl.addEventListener('click', marker.onClick); } this.container.querySelector('.gis-placeholder-map')?.appendChild(markerEl); this.markers.set(marker.id, markerEl); } /** * 添加多边形 */ addPolygon(polygon: Polygon) { if (this.provider === 'amap' && this.map) { const amapPolygon = new window.AMap.Polygon({ path: polygon.path.map(p => [p.lng, p.lat]), fillColor: polygon.fillColor || '#22c55e', strokeColor: polygon.strokeColor || '#166534', fillOpacity: polygon.fillOpacity || 0.3, strokeWeight: polygon.strokeWeight || 2, }); if (polygon.onClick) { amapPolygon.on('click', polygon.onClick); } this.map.add(amapPolygon); this.polygons.set(polygon.id, amapPolygon); } else if (this.provider === 'leaflet' && this.map) { const leafletPolygon = window.L.polygon( polygon.path.map(p => [p.lat, p.lng]), { color: polygon.strokeColor || '#166534', fillColor: polygon.fillColor || '#22c55e', fillOpacity: polygon.fillOpacity || 0.3, weight: polygon.strokeWeight || 2, } ).addTo(this.map); if (polygon.onClick) { leafletPolygon.on('click', polygon.onClick); } this.polygons.set(polygon.id, leafletPolygon); } } /** * 移除标记 */ removeMarker(id: string) { const marker = this.markers.get(id); if (!marker) return; if (this.provider === 'amap' && this.map) { this.map.remove(marker); } else if (this.provider === 'leaflet') { marker.remove(); } else if (this.provider === 'placeholder') { marker.remove(); } this.markers.delete(id); } /** * 移除多边形 */ removePolygon(id: string) { const polygon = this.polygons.get(id); if (!polygon) return; if (this.provider === 'amap' && this.map) { this.map.remove(polygon); } else if (this.provider === 'leaflet') { polygon.remove(); } this.polygons.delete(id); } /** * 设置中心点 */ setCenter(position: MapPosition, zoom?: number) { if (this.provider === 'amap' && this.map) { this.map.setCenter([position.lng, position.lat]); if (zoom) this.map.setZoom(zoom); } else if (this.provider === 'leaflet' && this.map) { this.map.setView([position.lat, position.lng], zoom || this.map.getZoom()); } } /** * 适应边界 */ fitBounds(bounds: MapBounds) { if (this.provider === 'amap' && this.map) { this.map.setBounds( new window.AMap.Bounds( [bounds.southwest.lng, bounds.southwest.lat], [bounds.northeast.lng, bounds.northeast.lat] ) ); } else if (this.provider === 'leaflet' && this.map) { this.map.fitBounds([ [bounds.southwest.lat, bounds.southwest.lng], [bounds.northeast.lat, bounds.northeast.lng] ]); } } /** * 缩放 */ setZoom(zoom: number) { if (this.provider === 'amap' && this.map) { this.map.setZoom(zoom); } else if (this.provider === 'leaflet' && this.map) { this.map.setZoom(zoom); } } /** * 获取当前缩放级别 */ getZoom(): number { if (this.provider === 'amap' && this.map) { return this.map.getZoom(); } else if (this.provider === 'leaflet' && this.map) { return this.map.getZoom(); } return 13; // 默认缩放 } /** * 清除所有标记 */ clearMarkers() { this.markers.forEach((marker, id) => { this.removeMarker(id); }); } /** * 清除所有多边形 */ clearPolygons() { this.polygons.forEach((polygon, id) => { this.removePolygon(id); }); } /** * 清除所有覆盖物 */ clearAll() { this.clearMarkers(); this.clearPolygons(); } /** * 销毁地图 */ destroy() { this.clearAll(); if (this.map) { if (this.provider === 'amap') { this.map.destroy(); } else if (this.provider === 'leaflet') { this.map.remove(); } this.map = null; } if (this.container) { this.container.innerHTML = ''; } } /** * 获取地图实例 */ getMapInstance() { return this.map; } /** * 获取当前提供商 */ getProvider(): MapProvider { return this.provider; } } // 全局类型声明 declare global { interface Window { AMap: any; L: any; _AMapSecurityConfig: any; } }