{"_id":"@3clear/basegis","_rev":"10-b639b7e6a2e2f17cb5b473a7aeaf9818","name":"@3clear/basegis","dist-tags":{"latest":"0.1.9"},"versions":{"0.1.0":{"name":"@3clear/basegis","version":"0.1.0","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.0","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"a566c162acc15d4a5bb9f924784b13db1b33c805","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.0.tgz","fileCount":8,"integrity":"sha512-tH3QLoja+utCtJ5egEk2DBOW+tXUTJ9Ad4IaFyhJQLOFV5aj9768zhIaizfViOqX5udOzoo8EZIXlaGDOxzmsg==","signatures":[{"sig":"MEQCIFrlziZ2CgYHAgARnBXBvn+Su1A12iQ7vhz8zTfjOtPtAiBDgkBDYD/eCGe+SemqVZBCwaBIVm/4FKT/FWDb5fulsQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":412500},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"0644e474e58f481f9753c46386cb77bcd0da5efd","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.4","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.21.1","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","@vitejs/plugin-vue":"^6.0.6"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4","pixi.js":"^7.4.3","leaflet-pixi-overlay":"^1.9.5"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.0_1782801295431_0.17970519108504668","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@3clear/basegis","version":"0.1.1","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.1","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"96c67d2eeadc2b401a4dbc29e2e23e7ebad25028","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.1.tgz","fileCount":8,"integrity":"sha512-MbD/4UbJDQ8Dm38I1X7YXR0QuY1izFqvcZ1FFVlx9tFQe/vB5J+hJSzXvKAvvUn6l+mtBOFADq0EpaJEe5xxjQ==","signatures":[{"sig":"MEUCIQCVh1ncviYZ1HCYc7wiD0gnf8sAlmOFeBmn5/dOGdKjngIgAiuZv4zzMZd6+uzN9qPii7uVuKZppC8CaxRl+SIuppk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":541045},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"3e18aba6124a996714240c5a04f7b1726cf124cb","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.4","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.21.1","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","@vitejs/plugin-vue":"^6.0.6"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4","pixi.js":"^7.4.3","leaflet-pixi-overlay":"^1.9.5"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.1_1783506444940_0.31368055308221576","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@3clear/basegis","version":"0.1.2","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.2","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"2076a249706e4186e9b69426f82515868ab4c878","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.2.tgz","fileCount":11,"integrity":"sha512-x+ELSxM9vFGnCJGOy+kbVcrx7Ojk997osoAGKcs6iWm+o+tY2QbvteYpvaJJ+5/deGlYLp/yIbl2Rg+XkhodKA==","signatures":[{"sig":"MEUCIQCFod04A2QyV7zUO1xpDHCAJ2WQDdecRAJlkci1PfCUNQIgC9kLFgJ35ZsW1LmpsTSGyDzCaVVo4j1nbeLm+cgUMZ0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1636995},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"a7a5d67d5dd21f26c410fe97677fb0477861519f","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.4","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.21.1","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.2_1784775920120_0.653621973347825","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@3clear/basegis","version":"0.1.3","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.3","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"7943e1ff533e14795aa0df26f3e7d952ee155302","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.3.tgz","fileCount":11,"integrity":"sha512-XbLNGp7ECgI9Qfvb+EVmNBgdSlNjtUAqY7bEpeTS3QonLBi0rb6xJWO5jj9Oqy31VnjO3os3GJJji/5AFsUZdQ==","signatures":[{"sig":"MEYCIQDVmVGJV5kgk/Dv49HvDOBB2mirSRO5QPvhM3Wlu4ElLAIhAI2YTHXPV6+jkjgxd3BvUXrqZd4nb0oEiklmsAC18jKw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1735716},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"52296f25edd0747f1c5fc7c1357eef33967c3142","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.8","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.22.3","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.3_1785918198536_0.5828400307574382","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@3clear/basegis","version":"0.1.4","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.4","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"8edd0f9d86790ec516c2bd6f9c0bc55b6a59a0ae","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.4.tgz","fileCount":11,"integrity":"sha512-uID6y9w7nd1gNsD2gYTGL4w8M1TWcmea0QdhJi/elqsjtrqGOS2QoksI3Bd/GlZL4A0wG08+UR+99BhZfcoi7A==","signatures":[{"sig":"MEUCIE8C9xUckeXJMhHnU3FvaltFY3h0glUq7KVHS9vYXThtAiEA9tiB/6md9HS94iS2QRdUoSq7LQyM2e4ZR635lUYNA8I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1887048},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"9be569d3cc518c366ceaf063de5f1cc5f6e3d89f","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.8","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.22.3","publishConfig":{"access":"restricted"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.4_1786618189638_0.9395807848741107","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@3clear/basegis","version":"0.1.5","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.5","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"0bb9c3ab8d7f40db0757a4a118675d91d2713a21","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.5.tgz","fileCount":14,"integrity":"sha512-VuB2Bgg7tJ/XCNFNhuof9CktnPKjCSrqg7KeWeyCfI2jwrz1pzXhxaS5ZXRl2RRUjqn13pBr52Ryw6fGzmi0SA==","signatures":[{"sig":"MEUCIGTSUWaFT4y/IHvdLTtAPJWAXN1MZG4MqTYzdFMOj4iJAiEA/eQSIIdVoqc+N5wXeKYbcyt7SPcKlDT/17DHimSxsnk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2368220},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"f66b95fa3cb98e1de933f52a0b4adbe799725aa4","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.8","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","html2canvas":"^1.4.1","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.5_1787901368291_0.15597738890588575","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"@3clear/basegis","version":"0.1.6","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.6","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"171d3f64a15d059aae1ebd2fdca3c46f52398321","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.6.tgz","fileCount":14,"integrity":"sha512-RbefEfDpmfkH+g3EGlbDCwZG7GY6eyLPi1pw2iU+lKH40eR1xeeeHMTtVliu/HN3lhYi2g7VUNG2n9mRpzDubw==","signatures":[{"sig":"MEQCIBvmOEyuZC1b050Rx3UerUiiNgRuxKypHHYa6HHJdFXdAiAOPZqqyYIenX7IfRnBqOzkLvaJDw2P/KgCXD2RzJBjKg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2375243},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"a6cdfd979338a302601ce4f8d1c7de0918131914","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.8","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","html2canvas":"^1.4.1","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.6_1788233198953_0.47930629589804763","host":"s3://npm-registry-packages-npm-production"}},"0.1.7":{"name":"@3clear/basegis","version":"0.1.7","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.7","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"fa4df42fe6124ef0d20ecbe04e930006c6ad2767","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.7.tgz","fileCount":14,"integrity":"sha512-7XVmS48ySfIDvhgHyFmecQbJxhsuQJ1fyu0ukF2jDz7keXqaT+Lxtzu7g4Y52c79pBDQ4HDjMslJoZlllTBLWw==","signatures":[{"sig":"MEYCIQCvP/1Asa2g/wEHmFUke752H3PQ5fcswTX7JvmAjHyJvQIhANRDsfgpnQKYsKNvNw86jb6r1NpMYNeGQFVOh4et5KK7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2397643},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"8c0bebfa8d074123f790d8b800dfd2cc2d1955f6","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.8","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","html2canvas":"^1.4.1","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.7_1788490337997_0.43614373890511016","host":"s3://npm-registry-packages-npm-production"}},"0.1.8":{"name":"@3clear/basegis","version":"0.1.8","keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.8","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"dist":{"shasum":"0b921471b37fb6394ee7b03322998eee6d5620fa","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.8.tgz","fileCount":14,"integrity":"sha512-ad3hu2Ax7W/VbzcJLl0a6DsibnJjE3CfQHvb9C3Xhst5i4Na83qu+QOa8wTo2hmjRxhGyAAyuGihEJRHfX7jVQ==","signatures":[{"sig":"MEYCIQCvY1uJ5OVTYavQmugOmFVjTYBcaWA3ReOG+QAmloStwQIhAPs26isnP3cHRpJhxdJy1KTgHoQpsjGRIJOWJMmgGaIh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2462510},"main":"./dist/basegis.js","type":"module","module":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./assets":"./dist/assets.js","./layers":"./dist/layers.js","./methods":"./dist/methods.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"gitHead":"549023820300beaab9cf40be9bded9362257a658","scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"_npmVersion":"10.9.8","description":"BaseGIS map engine adapter and GIS layer utilities.","directories":{},"sideEffects":["*.css","**/*.css"],"_nodeVersion":"22.22.3","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^6.4.2","pixi.js":"^7.4.3","d3-contour":"^4.0.2","html2canvas":"^1.4.1","@vitejs/plugin-vue":"^6.0.6","leaflet-pixi-overlay":"^1.9.5"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"_npmOperationalInternal":{"tmp":"tmp/basegis_0.1.8_1789024331103_0.22062169744569515","host":"s3://npm-registry-packages-npm-production"}},"0.1.9":{"name":"@3clear/basegis","version":"0.1.9","description":"BaseGIS map engine adapter and GIS layer utilities.","type":"module","module":"./dist/basegis.js","main":"./dist/basegis.js","exports":{".":"./dist/basegis.js","./methods":"./dist/methods.js","./layers":"./dist/layers.js","./assets":"./dist/assets.js","./style.css":"./dist/style.css","./package.json":"./package.json"},"sideEffects":["*.css","**/*.css"],"scripts":{"build":"\"$npm_node_execpath\" ../../node_modules/vite/bin/vite.js build","pack:local":"npm pack"},"peerDependencies":{"axios":"^1.15.1","leaflet":"^1.9.4"},"devDependencies":{"@vitejs/plugin-vue":"^6.0.6","d3-contour":"^4.0.2","html2canvas":"^1.4.1","leaflet-pixi-overlay":"^1.9.5","pixi.js":"^7.4.3","vite":"^6.4.2"},"publishConfig":{"access":"public"},"keywords":[],"author":"","license":"ISC","_id":"@3clear/basegis@0.1.9","gitHead":"549023820300beaab9cf40be9bded9362257a658","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-AJZX291RluY8Pzoh/szE64a9cgI78RU+j3SaHODa/0ddxClCwPQ8KZwMPB4I6v9RgyGsK9pJPt8B3PI+Or0NiQ==","shasum":"1a10c6f217dbe2ad0191761b4ae66addd1dccaa2","tarball":"https://registry.npmjs.org/@3clear/basegis/-/basegis-0.1.9.tgz","fileCount":14,"unpackedSize":2526974,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFGS2l8OYB3QkE9QNpR3kI35cgTJqcxlc2d+WQp5lv/PAiEA3F3JX07uIC/B5kDpOYU6GuNjwiK6+LGYQZWGLHXRxIs="}]},"_npmUser":{"name":"3clear","email":"qixw@3clear.com"},"directories":{},"maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/basegis_0.1.9_1789348826617_0.16028023889129228"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-30T06:34:55.286Z","modified":"2026-09-14T01:20:26.930Z","0.1.0":"2026-06-30T06:34:55.580Z","0.1.1":"2026-07-08T10:27:25.118Z","0.1.2":"2026-07-23T03:05:20.309Z","0.1.3":"2026-08-05T08:23:18.711Z","0.1.4":"2026-08-13T10:49:49.803Z","0.1.5":"2026-08-28T07:16:08.484Z","0.1.6":"2026-09-01T03:26:39.142Z","0.1.7":"2026-09-04T02:52:18.183Z","0.1.8":"2026-09-10T07:12:11.342Z","0.1.9":"2026-09-14T01:20:26.786Z"},"license":"ISC","keywords":[],"description":"BaseGIS map engine adapter and GIS layer utilities.","maintainers":[{"name":"3clear","email":"qixw@3clear.com"}],"readme":"# @3clear/basegis\n\n`@3clear/basegis` 是 3clear 一张图项目抽出的 GIS 能力包，通过统一入口 `BaseGIS` 封装 Cesium / Leaflet 常用 API。业务页面可复用同一套地图逻辑，复杂图层通过 `methods` 控制器组合公共方法；三维专有能力的支持范围见[能力支持说明](#能力支持说明)。\n\n当前包包含：\n\n- `BaseGIS`：初始化、引擎切换、视角与底图、[地图截图导出](#map-screenshot)、基础图形、Canvas 图标与扩散 Marker、GeoJSON、DEM、点击事件，以及统一风场、源解析传输、GPU 风场、三维体与剖面。\n- `methods`：多地图实时视角联动、独立线图层、台风路径、图片图层、网格图层、海量点、点位聚合、点位抽稀、等值线、风场等高级控制器。\n- `layers`：天地图、GeoServer 金字塔瓦片、WMS、WMTS 图层配置快捷构造器。\n- `assets`：站点、工厂、信息标记和台风中心 SVG 资源。\n- `style.css`：BaseGIS 自有图层、标注和截图框选样式，不内嵌 Leaflet 官方 CSS。\n\n## 能力总览\n\n以下按类型展示 `@3clear/basegis` 的主要业务能力；工具型辅助函数不单独作为能力卡展示。点击名称可跳转到下方详细说明，完整导入方式见[出口](#出口)。\n\n### 快速开始\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#base-gis-lifecycle\"><strong>初始化与生命周期</strong></a></small></td>\n      <td>\n        <small>创建并初始化 BaseGIS 实例，管理实例状态、尺寸刷新和资源销毁。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#map-init-config\"><strong>地图初始化配置</strong></a></small></td>\n      <td>\n        <small>说明构造参数、init() 参数优先级、完整配置分组，以及 setConfig() 的用法与生效规则。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#engine-switch\"><strong>切换引擎</strong></a></small></td>\n      <td>\n        <small>支持 Cesium / Leaflet 运行时切换，并保留当前视野、恢复托管图层。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-4\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#view-control-scene\"><strong>视角控制与场景模式</strong></a></small></td>\n      <td>\n        <small>支持视角缩放、重置、范围适配，以及 2D、2.5D、3D 场景模式切换。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#map-view-listener\"><strong>地图视角监听</strong></a></small></td>\n      <td>\n        <small>监听地图移动、缩放和视角变化的开始、过程与结束状态。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-36\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#map-click-events\"><strong>点击事件</strong></a></small></td>\n      <td>\n        <small>统一监听 Cesium / Leaflet 地图点击，返回坐标及命中目标信息。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 地图工具（2）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#map-screenshot\"><strong>地图截图导出</strong></a></small></td>\n      <td>\n        <small>支持当前视口、框选区域及指定范围截图，并导出 PNG 图片。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-40\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#map-view-link-controller\"><strong>多地图联动</strong></a></small></td>\n      <td>\n        <small>支持多地图实例注册，以及移动、缩放和视角实时联动。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-36\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 通用点位图层（5）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#basic-graphics\"><strong>基础图形与点位</strong></a></small></td>\n      <td>\n        <small>绘制点、线、面、文字和 Marker，支持 Cesium / Leaflet。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#point-large-layer-controller\"><strong>海量点</strong></a></small></td>\n      <td>\n        <small>使用批量渲染展示大规模点位数据。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-5\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#point-cluster-controller\"><strong>点位聚合</strong></a></small></td>\n      <td>\n        <small>根据地图视图和缩放层级对邻近点位进行聚合展示。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-29\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#point-density-controller\"><strong>点位抽稀</strong></a></small></td>\n      <td>\n        <small>根据当前视野裁剪点位，并按屏幕密度进行抽稀展示。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-8\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#marker-icons\"><strong>图标</strong></a></small></td>\n      <td>\n        <small>提供项目常用图标及告警扩散效果图标。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-41\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 行政区划边界图层（1）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#geojson-layer\"><strong>GeoJSON 图层</strong></a></small></td>\n      <td>\n        <small>支持 GeoJSON 点、线、面、行政区划边界，以及数据驱动的边线、填充和点样式。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-23\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 轨迹路径图层（2）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#line-layer-controller\"><strong>走航路径</strong></a></small></td>\n      <td>\n        <small>轨迹折线、逐顶点浓度着色、时间裁剪与沿线动画；底层支持实线/虚线、固定/流动渐变和图标引领的逐步出线。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-43\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#typhoon-path-controller\"><strong>台风路径</strong></a></small></td>\n      <td>\n        <small>实况路径、强度节点、当前中心、四象限风圈、多机构预报与路径播放；支持 Cesium / Leaflet 同一份规范化台风数据与引擎切换恢复。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-10\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 格点填色图层（2）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#image-layer-controller\"><strong>图片填色图层</strong></a></small></td>\n      <td>\n        <small>支持 PNG / JPG / WebP 等图片覆盖、灰度图及时次切换，可配套 GeoTIFF 取值。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-4\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#grid-layer-controller\"><strong>网格数值图层</strong></a></small></td>\n      <td>\n        <small>支持 GeoTIFF、灰度图和直接数值网格的数值注记与鼠标探针。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-4\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 等值线图层（2）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#contour-layer-controller\"><strong>通用等值线</strong></a></small></td>\n      <td>\n        <small>已有等值线数据，支持线平滑、线值标签、中心标注与标注避让。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-19\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#raster-contour-controller\"><strong>栅格等值线</strong></a></small></td>\n      <td>\n        <small>没有等值线数据，需要前端通过格点数据自动生成等值线，支持线平滑、标注避让，可选自动检测 H/L 中心。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-18\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 三维渲染（不支持2维）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#dem-terrain\"><strong>DEM 地形</strong></a></small></td>\n      <td>\n        <small>Cesium 专用，支持椭球地形、World Terrain 与地形夸张。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#source-transport\"><strong>源解析传输</strong></a></small></td>\n      <td>\n        <small>展示贡献弧线、灰色烟羽点云、移动烟团与目标汇聚体，体云复用现有体渲染。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-42\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#volume-rendering\"><strong>盒子体渲染</strong></a></small></td>\n      <td>\n        <small>对三维标量场进行体积采样与裁切，支持原位更新体数据及按屏幕位置取值。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-14\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#section-rendering\"><strong>切片/剖面渲染</strong></a></small></td>\n      <td>\n        <small>基于三维格点数据，按经度、纬度或气压层渲染切片与剖面。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-15\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 风场图层（2）\n\n<p><small>推荐使用 <a href=\"#unified-wind-layer\"><code>BaseGIS.upsertWindLayer</code></a> 统一入口：Cesium 自动使用 GPU，Leaflet 自动使用 Canvas，切换引擎时自动恢复风场。</small></p>\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#gpu-wind-layer\"><strong>GPU 风场</strong></a></small></td>\n      <td>\n        <small>Cesium 专用，使用 GPU 绘制风场粒子，支持地形采样。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-21\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#wind-field-methods\"><strong>Canvas 风场</strong></a></small></td>\n      <td>\n        <small>通过 Canvas 在 Cesium / Leaflet 上绘制风场粒子，支持风速底图。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-3\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### 影像瓦片图层（4）\n\n<table width=\"90%\">\n  <thead>\n    <tr>\n      <th width=\"20%\"><small>名称</small></th>\n      <th width=\"65%\"><small>介绍</small></th>\n      <th width=\"15%\"><small>查看</small></th>\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td><small><a href=\"#layer-config-builders\"><strong>天地图图层</strong></a></small></td>\n      <td>\n        <small>生成天地图矢量、影像、地形及注记图层配置。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#layer-config-builders\"><strong>GeoServer 金字塔瓦片</strong></a></small></td>\n      <td>\n        <small>生成 GeoServer 金字塔瓦片图层配置，支持标准瓦片地址模板。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#layer-config-builders\"><strong>WMS 图层</strong></a></small></td>\n      <td>\n        <small>生成 WMS 服务图层配置，支持图层名称及附加请求参数。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n    <tr>\n      <td><small><a href=\"#layer-config-builders\"><strong>WMTS 图层</strong></a></small></td>\n      <td>\n        <small>生成 WMTS 标准瓦片图层配置，可指定 Cesium / Leaflet 引擎支持范围。</small>\n      </td>\n      <td><small><a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small></td>\n    </tr>\n  </tbody>\n</table>\n\n### Assets 示例资源（4）\n\n<table>\n  <tbody>\n    <tr>\n      <td width=\"50%\">\n        <a href=\"#gis-marker-sample\"><strong>gisMarkerSample</strong></a><br>\n        <small>GIS Marker 示例资源 · <a href=\"http://61.50.111.214:31166/lgmap/test-page-2\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small>\n      </td>\n      <td width=\"50%\">\n        <a href=\"#typhoon-path-controller\"><strong>typhoonPathIcon</strong></a><br>\n        <small>台风路径默认中心 SVG · <a href=\"http://61.50.111.214:31166/lgmap/test-page-10\" target=\"_blank\" rel=\"noopener noreferrer\">查看示例</a></small>\n      </td>\n    </tr>\n    <tr>\n      <td width=\"50%\">\n        <a href=\"#gis-marker-sample\"><strong>gisFactoryMarker</strong></a><br>\n        <small>工厂标记 SVG，可配合图片与数值图标使用</small>\n      </td>\n      <td width=\"50%\">\n        <a href=\"#gis-marker-sample\"><strong>gisInfoMarker</strong></a><br>\n        <small>信息标记 SVG，可配合图片与名称图标使用</small>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n## 安装\n\n```bash\nnpm install @3clear/basegis leaflet axios\n```\n\n`d3-contour`、`pixi.js`、`leaflet-pixi-overlay` 和 `html2canvas` 已随 BaseGIS 构建产物发布，业务项目不需要单独安装。其中 Pixi 相关代码只在首次使用 Leaflet 海量点能力时按需加载，`html2canvas` 只在首次调用 Leaflet 截图时按需加载。\n\n在应用入口引入 BaseGIS 与自有样式：\n\n```js\nimport { BaseGIS } from '@3clear/basegis'\nimport '@3clear/basegis/style.css'\n```\n\n外部依赖版本以 `package.json` 为准：当前为 `leaflet ^1.9.4`、`axios ^1.15.1`。\n\n当前限制：完整 BaseGIS 入口中的剖面工具仍在模块加载时访问 `Cesium.Cartesian3`，因此即使只创建 Leaflet 地图，也需在导入 BaseGIS 前加载下文的 Cesium 脚本，否则会报 `Cesium is not defined`。这是既有的引擎依赖问题，与本次 Leaflet CSS 共用处理无关；纯原生 Leaflet 不受此限制。\n\nLeaflet JS 和 `leaflet/dist/leaflet.css` 都由宿主依赖提供。BaseGIS 保留对官方 CSS 的导入，因此上面的原有用法仍然有效；官方规则不再另行内嵌到包的 `style.css` 中。\n\n如果项目同时使用原生 Leaflet，可以在应用入口统一引入：\n\n```js\nimport L from 'leaflet'\nimport 'leaflet/dist/leaflet.css'\nimport { BaseGIS } from '@3clear/basegis'\nimport '@3clear/basegis/style.css'\n```\n\n确保 BaseGIS 与业务代码解析到同一份安装的 Leaflet，宿主构建器即可复用同路径的 JS / CSS。不同地图使用各自容器；不需要挂载 `window.L` 或注册 Vue 插件，其他模块需要使用 `L` / `BaseGIS` 时仍按需 import。纯原生页面若不导入 BaseGIS JS，必须自行引入 `leaflet/dist/leaflet.css`，仅引入包的 `style.css` 不包含官方规则。\n\nCesium 不随 npm 包发布。使用 Cesium 时，宿主项目需自行加载 `Cesium.js` 和 `Widgets/widgets.css`，并保留 `Workers`、`Assets` 等完整静态目录；初始化前必须能访问 `window.Cesium`。`config.engine.cesium.scriptUrl/cssUrl` 不会自动注入脚本和样式。\n\n例如，将完整 Cesium 资源放入 `public/lib/Cesium` 后，在 Vite 的 `index.html` 中、应用入口脚本之前添加：\n\n```html\n<script>window.CESIUM_BASE_URL = '%BASE_URL%lib/Cesium/'</script>\n<link rel=\"stylesheet\" href=\"%BASE_URL%lib/Cesium/Widgets/widgets.css\">\n<script src=\"%BASE_URL%lib/Cesium/Cesium.js\"></script>\n```\n\n使用 GeoTIFF 数据时，宿主还需提前提供 `window.GeoTIFF`。本文 `/data/...`、`/mock/...` 均为示例数据地址，不包含在 npm 包中，请替换为项目真实地址；部署在子路径时，应结合宿主的 `import.meta.env.BASE_URL` 生成静态资源 URL。\n\n## 出口\n\n```js\n// 主入口\nimport { BaseGIS } from '@3clear/basegis'\n\n// 高级能力控制器\nimport {\n  LineLayerController,\n  MapViewLinkController,\n  TyphoonPathController,\n  ImageLayerController,\n  toImageLayerArea,\n  GridLayerController,\n  PointLargeLayerController,\n  PointClusterController,\n  PointDensityController,\n  ContourLayerController,\n  RasterContourController,\n  WindFieldMethods,\n} from '@3clear/basegis/methods'\n\n// 图层配置构造器\nimport {\n  createTiandituLayer,\n  createTiandituTileSource,\n  createGeoserverPyramidLayer,\n  createWmsLayer,\n  createWmtsLayer,\n} from '@3clear/basegis/layers'\n\n// 示例资源\nimport {\n  gisMarkerSample,\n  gisFactoryMarker,\n  gisInfoMarker,\n  typhoonPathIcon,\n} from '@3clear/basegis/assets'\n```\n\n## 快速开始\n\n```vue\n<template>\n  <div id=\"map\" class=\"map\"></div>\n</template>\n\n<script setup>\nimport { onBeforeUnmount, onMounted } from 'vue'\nimport { BaseGIS } from '@3clear/basegis'\nimport '@3clear/basegis/style.css'\n\nlet mapCore = null\n\nonMounted(() => {\n  mapCore = new BaseGIS({\n    // 可选 cesium / leaflet。不传时使用默认配置 active: 'cesium'。\n    engineType: 'cesium',\n    // 也可以传 container: HTMLElement。\n    containerId: 'map',\n    config: {\n      // 地图 / 底图投影与无元数据栅格默认源投影分开配置。\n      crs: 'EPSG:3857',\n      sourceProjectionCrs: 'EPSG:3857',\n      view: {\n        // 可选 2d / 2.5d / 3d。\n        defaultSceneMode: '3d',\n        initialView: {\n          center: [104, 35],\n          // Cesium 使用 height，Leaflet 使用 zoom。\n          height: 5000000,\n          zoom: 5,\n          // Cesium 默认保持垂直俯视。\n          pitch: -90,\n        },\n      },\n      basemap: {\n        // 默认内置值：tianditu-imagery。\n        defaultVisibleId: 'tianditu-imagery',\n      },\n    },\n  })\n\n  const result = mapCore.init()\n  if (!result.success) {\n    console.warn(result.message)\n  }\n})\n\nonBeforeUnmount(() => {\n  mapCore?.destroy()\n})\n</script>\n\n<style scoped lang=\"scss\">\n.map {\n  width: 100%;\n  height: 100vh;\n}\n</style>\n```\n\n以下 API 示例默认 `mapCore` 已初始化成功；示例中的业务数据、图片和服务地址需由页面准备。组件卸载时先销毁控制器、移除页面监听，再调用 `mapCore.destroy()`。\n\n## 返回值约定\n\n操作方法通常返回统一结果对象；图片、GeoJSON、风场等异步加载方法应使用 `await`：\n\n```js\n{\n  success: true,\n  message: 'operation message',\n  data: {}\n}\n```\n\n失败时：\n\n```js\n{\n  success: false,\n  message: 'error message',\n  code: 'NOT_INITIALIZED'\n}\n```\n\n`getEngineType()`、`getConfig()`、`getMapInstance()` 等读取方法直接返回值；`createMarkerIcon()` 直接返回图标参数或 `null`。控制器的 `getState()` 也返回状态对象，不能一律按 `result.data` 读取。\n\n<a id=\"map-init-config\"></a>\n\n## 地图初始化与运行时配置\n\n`BaseGIS` 的初始化参数分为两类：需要长期保留的默认配置放在构造函数的 `config` 中；只想覆盖本次初始化时，传给 `init(options)`。地图已经创建后，`setConfig()` 只更新实例保存的配置，不会直接改变当前画面；初始化默认值通常需要重新 `init()` 才会生效。\n\n| 配置入口 | 适用场景 | 是否写入运行时配置 | 是否立即重建地图 |\n| --- | --- | --- | --- |\n| `new BaseGIS({ ... })` | 创建实例并设置默认引擎、容器和整套配置。 | 是 | 否，仍需调用 `init()`。 |\n| `init(options)` | 创建或重新创建地图，并单次覆盖容器、引擎、CRS、场景模式或初始视角。 | `engineType` 会写入；`crs / sceneMode / initialView` 不写入。 | 是 |\n| `setConfig(config)` | 修改实例保存的配置，供后续初始化或配置查询使用。 | 是 | 否 |\n\n### 构造参数\n\n```js\nconst mapCore = new BaseGIS({\n  engineType: 'cesium',\n  containerId: 'map',\n  config: {\n    crs: 'EPSG:3857',\n    sourceProjectionCrs: 'EPSG:3857',\n    view: {\n      defaultSceneMode: '3d',\n      initialView: {\n        center: [104, 35],\n        height: 5000000,\n        zoom: 5,\n        heading: 0,\n        pitch: -90,\n        roll: 0,\n      },\n    },\n  },\n})\n```\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `engineType` | `'cesium' \\| 'leaflet'` | `config.engine.active`，内置为 `cesium` | 默认引擎。该参数最终覆盖 `config.engine.active`。 |\n| `container` | `HTMLElement \\| string` | - | 地图容器 DOM，也兼容传容器 id 字符串。 |\n| `containerId` | `string` | - | 地图容器 id。与 `container` 二选一即可。 |\n| `config` | `Object` | 内置配置 | 推荐的配置入口，与内置配置做深合并。 |\n| `crs / sourceProjectionCrs` | `string` | 见下文 | 分别表示地图 / 底图投影和无元数据栅格的默认源投影，支持 `EPSG:4326` / `EPSG:3857`。 |\n| `engine / view / basemap / dem / sourceTransport` | `Object` | - | 兼容直接写在构造参数顶层；`crs / sourceProjectionCrs` 也可如此传入。推荐统一放入 `config`，同名时 `config` 中的值优先。 |\n\n### `init(options)` 参数与优先级\n\n```js\nconst result = mapCore.init({\n  // 都是选填；构造时已经设置过的内容不需要重复传。\n  containerId: 'map',\n  engineType: 'cesium',\n  crs: 'EPSG:3857',\n  sceneMode: '3d',\n  initialView: {\n    center: [104, 35],\n    height: 5000000,\n    zoom: 5,\n    pitch: -90,\n  },\n})\n\nif (!result.success) {\n  console.warn(result.message)\n}\n```\n\n| 参数 | 类型 | 说明 |\n| --- | --- | --- |\n| `container` | `HTMLElement \\| string` | 本次使用的容器 DOM 或容器 id，优先级最高。 |\n| `containerId` | `string` | 本次使用的容器 id。 |\n| `engineType` | `'cesium' \\| 'leaflet'` | 本次使用的引擎，同时更新实例保存的 `engine.active`。 |\n| `crs` | `'EPSG:4326' \\| 'EPSG:3857'` | 本次创建地图使用的 CRS；只覆盖本次初始化，不写回 `config.crs`。 |\n| `sceneMode` | `'2d' \\| '2.5d' \\| '3d'` | 本次初始化的场景模式；Leaflet 只支持 `2d`。不会写回 `config.view.defaultSceneMode`。 |\n| `initialView` | `Object` | 本次初始化的视角。不会写回 `config.view.initialView`。 |\n\n`init({ config: ... })` 不是有效写法；`init()` 不会合并 `config`。持久配置必须在构造函数中传入，或先调用 `setConfig()`。\n\n各参数的实际取值顺序如下，左侧优先级更高：\n\n| 项目 | 取值优先级 |\n| --- | --- |\n| 容器 | `init.container` → `init.containerId` → 构造函数 `container` → 构造函数 `containerId` |\n| 引擎 | `init.engineType` → 实例当前引擎（由构造函数 `engineType` 或 `config.engine.active` 得到）→ `cesium` |\n| 地图 CRS | `init.crs` → `config.crs` → 旧版引擎默认（Leaflet `EPSG:4326`，Cesium `EPSG:3857`） |\n| 场景模式 | Cesium：`init.sceneMode` → `config.view.defaultSceneMode` → `3d`；Leaflet 始终为 `2d` |\n| 初始视角 | `init.initialView` → `config.view.initialView` → 引擎内置安全视角 |\n\n重复调用 `init()` 时会先销毁旧 adapter，再创建新地图；BaseGIS 托管图层会异步恢复。`init()` 本身同步返回初始化结果，后续逻辑依赖图层恢复时还需等待：\n\n```js\nconst result = mapCore.init()\nif (result.success) {\n  const restore = await mapCore.whenReady()\n  console.log(restore.restored, restore.failed)\n}\n```\n\n### 完整配置分组\n\n`config` 顶层包含两个 CRS 值和以下五个配置分组。页面不要直接依赖包内部的 `src/gis/config` 文件；需要查看当前实例最终合并后的配置时，使用 `mapCore.getConfig()`。\n\n| 配置项 / 组 | 用途 | 主要生效时机 |\n| --- | --- | --- |\n| `crs` | 地图平面投影与底图瓦片矩阵。 | `init()` / 重新 `init()` |\n| `sourceProjectionCrs` | 无 CRS 元数据栅格的默认源投影。 | 后续创建或更新栅格图层时 |\n| `engine` | 默认引擎、引擎接入元数据及 Cesium 渲染质量。 | `init()` / 重新 `init()` |\n| `view` | 默认场景模式和初始视角。 | `init()` / 重新 `init()` |\n| `basemap` | 初始底图、可切换底图列表及内置服务元数据。 | `init()`；列表也供 `setBasemapById()` 查询 |\n| `dem` | Cesium 默认地形及可切换地形列表。 | Cesium `init()`；列表也供 `loadDEMById()` 查询 |\n| `sourceTransport` | 源解析传输图层的实例级默认视觉参数。 | 创建 adapter 时锁定；`setConfig()` 后需重新 `init()`，新 adapter 创建图层时才会读取 |\n\n#### `crs` 与 `sourceProjectionCrs`\n\n| 配置项 | 内置默认值 | 说明 |\n| --- | --- | --- |\n| `crs` | `''` | 地图平面和底图瓦片矩阵；空值保留旧版引擎默认，Leaflet 为 `EPSG:4326`，Cesium 为 `EPSG:3857`。 |\n| `sourceProjectionCrs` | `'EPSG:4326'` | PNG / JPG / 灰度图 / 数值网格不携带 CRS 元数据时的默认源投影。 |\n\n两个值独立：修改底图 `crs` 不会重新解释栅格数据，修改 `sourceProjectionCrs` 也不会改变底图矩阵。`area`、视角以及点线面业务坐标始终使用 WGS84 经纬度。Cesium 中 `crs` 控制 2D / Columbus 平面投影和内置底图矩阵，不改变 3D 地球的 WGS84 坐标。\n\n栅格源投影的取值顺序是：单个数据源的 `sourceProjectionCrs`（兼容旧名 `sourceProjection`）→ GeoTIFF 可识别 GeoKey → BaseGIS `sourceProjectionCrs` → 内置 `EPSG:4326`。GeoTIFF 已声明但当前不支持的 CRS 会返回错误，不会被全局默认值覆盖。修改 `crs` 后必须重新 `init()`；修改 `sourceProjectionCrs` 只影响之后新建或重新加载且没有显式投影的栅格，不会自动重解释已加载数据。\n\n#### `engine` 引擎配置\n\n| 配置项 | 内置默认值 | 说明 |\n| --- | --- | --- |\n| `engine.active` | `'cesium'` | 默认引擎；构造参数 `engineType` 或 `init({ engineType })` 的优先级更高。 |\n| `engine.cesium.sourceType` | `'local-script'` | Cesium 接入方式说明字段。 |\n| `engine.cesium.scriptUrl` | `'/lib/Cesium/Cesium.js'` | Cesium 脚本地址说明字段；BaseGIS 不会自动加载该脚本。 |\n| `engine.cesium.cssUrl` | `'/lib/Cesium/Widgets/widgets.css'` | Cesium 样式地址说明字段；BaseGIS 不会自动加载该样式。 |\n| `engine.cesium.renderQuality.maximumDevicePixelRatio` | `2` | Cesium 最大设备像素比，实际值限制在 `1～3`；越高越清晰，也越耗 GPU。 |\n| `engine.cesium.renderQuality.fxaa` | `true` | 是否启用 Cesium FXAA。 |\n| `engine.cesium.renderQuality.msaaSamples` | `4` | Cesium MSAA 采样数，取整并限制在 `1～8`。 |\n| `engine.leaflet.sourceType` | `'npm'` | Leaflet 接入方式说明字段。 |\n| `engine.leaflet.packageName` | `'leaflet'` | Leaflet 依赖包名说明字段。 |\n\n`scriptUrl / cssUrl` 只是接入元数据。Cesium 资源的实际加载方法见[安装](#安装)。渲染质量在创建 Cesium Viewer 时读取，修改后需要重新 `init()`。\n\n#### `view` 视角配置\n\n| 配置项 | 内置默认值 | Cesium | Leaflet | 说明 |\n| --- | --- | --- | --- | --- |\n| `view.defaultSceneMode` | `'3d'` | 使用 | 只接受 `2d` | 默认场景，可选 `2d / 2.5d / 3d`。 |\n| `view.initialView.center` | `[121.4737, 31.2304]` | 使用 | 使用 | `[经度, 纬度]`。 |\n| `view.initialView.height` | `1800000` | 使用 | 忽略 | Cesium 相机高度，单位米。 |\n| `view.initialView.zoom` | `7` | 忽略 | 使用 | Leaflet 缩放级别。 |\n| `view.initialView.heading` | `0` | 使用 | 忽略 | Cesium 航向角，单位度。 |\n| `view.initialView.pitch` | `-90` | 使用 | 忽略 | Cesium 俯仰角，单位度；`-90` 表示垂直俯视。 |\n| `view.initialView.roll` | `0` | 使用 | 忽略 | Cesium 翻滚角，单位度。 |\n\n#### `basemap` 底图配置\n\n| 配置项 | 内置默认值 | 说明 |\n| --- | --- | --- |\n| `basemap.defaultVisibleId` | `'tianditu-imagery'` | 初始化时从 `basemap.list` 选择的底图 id。 |\n| `basemap.defaultAnnotationId` | `'tianditu-vector-label'` | 当前为保留字段，初始化流程尚未读取；注记应通过所选底图项的 `annotationResourceKey` 配置，天地图也会按底图资源自动推断注记。 |\n| `basemap.annotationOnTop` | `false` | 设为 `true` 后，底图注记显示在图片图层上方；双引擎的图片更新、底图切换和引擎恢复均自动维持顺序。 |\n| `basemap.list` | 内置天地图、WMS、WMTS 示例列表 | 底图资源数组；完整默认项和字段见下方[默认底图配置](#default-basemap-config)。外部传入数组会整体替换内置数组。 |\n| `basemap.providers` | 内置天地图 provider | 内置服务的地址、子域名、token 池及资源映射。当前适配器读取包内 provider，构造参数或 `setConfig()` 中的覆盖值尚不会生效；自定义服务请在 `basemap.list` 中配置 URL。 |\n\n把地名、边界等瓦片注记显示在天气图片上方：\n\n```js\nconst mapCore = new BaseGIS({\n  containerId: 'map',\n  config: {\n    basemap: {\n      defaultVisibleId: 'tianditu-imagery',\n      annotationOnTop: true,\n    },\n  },\n})\n```\n\nLeaflet 将注记放到独立的 `440` 层，位于图片与等值线、Marker 之间，且不拦截鼠标交互。Cesium 在影像集合变化时将当前注记置顶；开启此项后，区域灰度图也使用 Canvas 着色后的影像管线，避免 GroundPrimitive 覆盖整个影像栈。该设置只控制底图瓦片注记与图片的关系，不会将注记抬到三维物体或后处理效果之上。配置在 `init()` 时读取；修改后需重新 `init()`，单独 `setConfig()` 不立即改变当前地图。\n\n离线回归页面：启动开发服务后访问 `/lgmap/scripts/test-basemap-annotations.html`，使用合成瓦片和真实截图像素验证开关、图片替换、底图切换、引擎恢复及销毁清理。\n\n#### `dem` 地形配置\n\n| 配置项 | 内置默认值 | 说明 |\n| --- | --- | --- |\n| `dem.defaultEnabled` | `true` | Cesium 初始化时是否加载默认 DEM；`false` 时跳过。 |\n| `dem.defaultVisibleId` | `'ellipsoid-flat'` | 默认 DEM id，必须能在 `dem.list` 中找到、`visible !== false` 且支持 Cesium，否则初始化失败。 |\n| `dem.list` | 平面地形、Cesium World Terrain、天地图 DEM 占位项 | DEM 资源数组；外部传入时整体替换。列表项常用字段为 `id / name / sourceType / factory / url / visible / engineSupport / options`，详细用法见[DEM 地形](#dem-terrain)。 |\n\nLeaflet 不加载 DEM。`cesium-world-terrain` 需要 Cesium Ion 能力；`tianditu-dem` 是待补真实服务地址的占位项。\n\n#### 常用地图初始化配置示例\n\n下面示例把真正参与地图创建的常用配置集中写在一起。未传的字段继续使用上表中的内置默认值：\n\n```js\nconst mapCore = new BaseGIS({\n  containerId: 'map',\n  config: {\n    // 地图瓦片与无元数据栅格的默认投影分开管理。\n    crs: 'EPSG:3857',\n    sourceProjectionCrs: 'EPSG:3857',\n    engine: {\n      active: 'cesium',\n      cesium: {\n        renderQuality: {\n          maximumDevicePixelRatio: 2,\n          fxaa: true,\n          msaaSamples: 4,\n        },\n      },\n    },\n    view: {\n      defaultSceneMode: '3d',\n      initialView: {\n        center: [104, 35],\n        height: 5000000,\n        zoom: 5,\n        heading: 0,\n        pitch: -90,\n        roll: 0,\n      },\n    },\n    basemap: {\n      defaultVisibleId: 'tianditu-imagery',\n    },\n    dem: {\n      defaultEnabled: true,\n      defaultVisibleId: 'ellipsoid-flat',\n    },\n  },\n})\n\nconst result = mapCore.init()\nif (!result.success) {\n  console.warn(result.message)\n}\n```\n\n#### `sourceTransport` 源解析传输默认配置\n\n这一组不是地图容器或相机初始化参数，而是 Cesium [源解析传输](#source-transport)图层的实例级默认值。\n\n| 配置项 | 内置默认值 | 说明 |\n| --- | --- | --- |\n| `sourceTransport.visible` | `true` | 新建图层默认是否显示。 |\n| `sourceTransport.running` | `true` | 新建图层默认是否播放烟羽动画。 |\n| `sourceTransport.colors` | 内置 7 色数组 | 多来源默认色板。 |\n| `sourceTransport.path` | 内置对象 | 弧线路径分段、高度与弯曲参数。 |\n| `sourceTransport.line` | 内置对象 | 线宽、透明度、辉光、收尖和命中宽度。 |\n| `sourceTransport.smoke` | 内置对象 | 烟羽数量、大小、透明度、扩散和速度。 |\n| `sourceTransport.sourceNode` | 内置对象 | 来源节点大小与标签数量。 |\n| `sourceTransport.targetNode` | 内置对象 | 目标节点颜色与环半径。 |\n| `sourceTransport.volume` | 内置对象 | 目标体云开关、网格尺寸、渲染参数及色带。 |\n| `sourceTransport.interaction` | 内置对象 | 交互开关、命中容差和鼠标样式。 |\n| `sourceTransport.fog` | 内置对象 | 雾效开关与密度。 |\n| `sourceTransport.camera` | 内置对象 | 定位图层时的俯仰角、高度和动画时长。 |\n\n单个图层传入的同名参数优先于这里的实例默认值。\n\n### 配置合并规则\n\n构造函数和 `setConfig()` 使用同一套合并规则：\n\n| 数据类型 | 合并方式 |\n| --- | --- |\n| 普通对象 | 按层级递归合并，只传一个叶子字段不会删除同组其他字段。 |\n| 数组 | 整体替换，不会追加；`basemap.list`、`dem.list`、`sourceTransport.colors` 都遵循此规则。 |\n| 基本类型、函数及其他值 | 使用新值替换旧值。 |\n| 未传字段 | 保留当前配置中的值。 |\n\n配置会被克隆后保存，不会直接修改包内默认配置。`getConfig()` 返回当前实例持有的配置对象引用，建议只读；修改配置统一调用 `setConfig()`。\n\n例如，只覆盖 Cesium 像素比不会丢失 `fxaa` 和 `msaaSamples`：\n\n```js\nmapCore.setConfig({\n  engine: {\n    cesium: {\n      renderQuality: {\n        maximumDevicePixelRatio: 1.5,\n      },\n    },\n  },\n})\n```\n\n如果要在现有底图列表后追加一项，必须自行保留原数组：\n\n```js\nconst currentConfig = mapCore.getConfig()\n\nmapCore.setConfig({\n  basemap: {\n    list: [\n      ...currentConfig.basemap.list,\n      {\n        id: 'custom-xyz',\n        name: '自定义 XYZ',\n        category: 'basemap',\n        type: 'xyz',\n        provider: 'custom',\n        url: 'https://example.com/tiles/{z}/{x}/{y}.png',\n        engineSupport: ['cesium', 'leaflet'],\n      },\n    ],\n  },\n})\n```\n\n### `setConfig()` 怎么用\n\n`setConfig(overrideConfig)` 是同步方法，没有统一结果对象或其他返回值。它只更新 `BaseGIS` 实例保存的配置，不会把新配置推送给已经创建的 adapter，也不会自动改变当前场景、视角、底图、DEM 或渲染质量。`crs / sourceProjectionCrs` 会归一化为 BaseGIS 支持的名称；修改 `crs` 后需重新 `init()`，修改 `sourceProjectionCrs` 只作为后续新数据的回退值。`engine.active` 是另一个需要特别注意的字段：它会同步实例记录的引擎类型，但不会真正替换当前底层地图。\n\n需要让整套初始化配置生效时，直接再次 `init()`，不要先调用 `destroy()`；否则托管图层快照会被清空：\n\n```js\nmapCore.setConfig({\n  engine: {\n    cesium: {\n      renderQuality: {\n        maximumDevicePixelRatio: 1.5,\n        fxaa: true,\n        msaaSamples: 4,\n      },\n    },\n  },\n  view: {\n    defaultSceneMode: '2d',\n    initialView: {\n      center: [116.4, 39.9],\n      height: 1800000,\n      zoom: 7,\n      pitch: -90,\n    },\n  },\n})\n\nconst result = mapCore.init()\nif (result.success) {\n  await mapCore.whenReady()\n}\n```\n\n如果目标只是立即操作当前地图，不要用 `setConfig() + init()` 代替已有的实时 API：\n\n| 目标 | 推荐 API | 是否重建地图 |\n| --- | --- | --- |\n| 切换 Cesium / Leaflet | `await mapCore.setEngine(engineType)` | 是，并自动恢复托管图层 |\n| 切换当前场景模式 | `mapCore.setSceneMode({ mode })` | 否 |\n| 移动当前视角 | `mapCore.setInitialView(view)` | 否 |\n| 切换当前底图 | `mapCore.setBasemapById(id)` 或 `mapCore.setBasemap(payload)` | 否 |\n| 切换当前 DEM | `mapCore.loadDEMById(id)`、`mapCore.loadDefaultDEM()` 或 `mapCore.loadDEM(payload)` | 否 |\n| 修改当前或新建源解析传输层 | `mapCore.updateSourceTransportLayer(payload)` 或 `mapCore.upsertSourceTransportLayer(payload)` | 否 |\n| 修改 Cesium 渲染质量或整套初始化默认值 | `setConfig()` 后重新 `init()` | 是 |\n\n特别注意：不要用 `setConfig({ engine: { active: 'leaflet' } })` 切换已经显示的地图；应直接调用 `setEngine('leaflet')`。前者会让 `getEngineType()` 变成 `leaflet`，但底层仍可能是 Cesium，随后调用相同目标的 `setEngine('leaflet')` 还会被当作“已经是当前引擎”而跳过。若已经这样修改，应调用 `init()` 重建并校正地图实例。\n\n<a id=\"default-basemap-config\"></a>\n\n### 默认底图配置\n\n不传 `config.basemap` 时，内置默认配置如下：\n\n```js\nbasemap: {\n  defaultVisibleId: 'tianditu-imagery',\n  // 当前为保留字段，初始化时不会读取。\n  defaultAnnotationId: 'tianditu-vector-label',\n  annotationOnTop: false,\n}\n```\n\n默认底图列表：\n\n| id | 名称 | category | enabled | type | provider | resourceKey | 默认用途 | 引擎支持 |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n| `tianditu-vector` | 天地图矢量底图 | `basemap` | `true` | `wmts` | `tianditu` | `vector` | 可作为 `defaultVisibleId` | Cesium / Leaflet |\n| `tianditu-imagery` | 天地图影像底图 | `basemap` | `true` | `wmts` | `tianditu` | `imagery` | 内置 `defaultVisibleId` | Cesium / Leaflet |\n| `tianditu-terrain` | 天地图地形底图 | `basemap` | `true` | `wmts` | `tianditu` | `terrain` | 可作为 `defaultVisibleId` | Cesium / Leaflet |\n| `tianditu-vector-label` | 天地图矢量注记 | `annotation` | `true` | `wmts` | `tianditu` | `vectorLabel` | 可作为底图的 `annotationResourceKey` | Cesium / Leaflet |\n| `tianditu-terrain-label` | 天地图地形注记 | `annotation` | `true` | `wmts` | `tianditu` | `terrainLabel` | 可作为底图的 `annotationResourceKey` | Cesium / Leaflet |\n\n说明：\n\n- `defaultVisibleId` 应指向 `category: 'basemap'` 的底图。\n- `defaultVisibleId` 找不到可用项时，会回退到 `basemap.list` 中第一个 `enabled !== false`、分类和引擎均匹配的底图；仍找不到时使用内置天地图矢量兜底配置。\n- `defaultAnnotationId` 当前只是保留字段，不参与初始化。要指定注记，给底图项设置 `annotationResourceKey`；天地图矢量、影像和地形底图未显式设置时，也会分别推断对应注记资源。\n- `tianditu-terrain` 是天地图地形底图瓦片，不是 Cesium 的 DEM 高程地形；如果要控制 Cesium terrainProvider，请看后文 DEM。\n- `geoserver-wmts-sample` 和 `geoserver-wms-sample` 默认 `enabled: false`，只是配置格式示例；如果要作为默认底图，需要替换真实服务地址并改为 `enabled: true`。\n- 天地图 provider 内置资源还包括 `imageryLabel`；默认 `basemap.list` 没有单独注册影像注记 id，但 `resourceKey: 'imagery'` 会自动推断使用 `imageryLabel` 注记。\n- 内置天地图会根据地图 `crs` 选择 `_c` 或 `_w` 瓦片矩阵；自定义 XYZ / WMTS / WMS 的 URL 和矩阵必须由业务保证与地图 `crs` 一致。地图已创建后不能通过普通底图切换改变 CRS，必须修改配置并重新 `init()`。\n\n### 底图切换\n\n底图可以来自 `config.basemap.list`，也可以直接传配置对象。当前适配层支持：\n\n- 天地图：`provider: 'tianditu'` 或传 `resourceKey`\n- URL 模板瓦片：`type: 'wmts' / 'xyz' / 'tile'`\n- WMS：`type: 'wms'`\n\n按配置 id 切换：\n\n```js\nconst result = mapCore.setBasemapById('tianditu-imagery')\nif (!result.success) {\n  console.warn(result.message)\n}\n```\n\n直接传底图配置：\n\n```js\n\nmapCore.setBasemap({\n  id: 'custom-xyz',\n  name: '自定义 XYZ',\n  category: 'basemap',\n  type: 'xyz',\n  provider: 'custom',\n  url: 'https://example.com/tiles/{z}/{x}/{y}.png',\n  minZoom: 0,\n  maxZoom: 18,\n  subdomains: ['a', 'b', 'c'],\n})\n\nmapCore.setBasemap({\n  id: 'custom-wms',\n  name: '自定义 WMS',\n  category: 'basemap',\n  type: 'wms',\n  provider: 'custom',\n  serviceUrl: 'https://example.com/geoserver/wms',\n  layers: 'workspace:layer',\n  parameters: {\n    transparent: true,\n    format: 'image/png',\n    version: '1.1.1',\n  },\n})\n```\n\n\n方法说明：\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `setBasemapById(id)` | 底图 id | 从 `config.basemap.list` 查找底图并切换。会校验 `category / engineSupport / enabled`。 |\n| `setBasemap(payload)` | 底图配置对象 | 直接切换到底图配置。 |\n\n常用底图参数：\n\n| 参数 | 是否必填 | 说明 |\n| --- | --- | --- |\n| `id` | 建议必填 | 底图唯一 id。 |\n| `name` | 选填 | 底图名称。 |\n| `category` | 建议传 `basemap` | `setBasemapById` 会拒绝非 `basemap` 分类。 |\n| `type` | 自定义服务必填 | `wmts`、`xyz`、`tile`、`wms`。 |\n| `provider` | 选填 | 天地图传 `tianditu`，自定义服务传 `custom`。 |\n| `resourceKey` | 天地图必填 | `vector`、`imagery`、`terrain` 等。 |\n| `annotationResourceKey` | 选填 | 天地图注记资源，如 `vectorLabel`、`imageryLabel`。 |\n| `url` | URL 模板必填 | `wmts/xyz/tile` 使用，支持 `{z}/{x}/{y}` 模板。 |\n| `serviceUrl` | WMS 必填 | WMS 服务地址；也可用 `url`。 |\n| `layers` | WMS 必填 | WMS 图层名。 |\n| `parameters` | 选填 | WMS 附加参数。 |\n| `engineSupport` | 选填 | 支持的引擎列表，如 `['cesium', 'leaflet']`。 |\n\n<a id=\"layer-config-builders\"></a>\n\n## 图层配置构造器\n\n`layers` 出口提供四个快捷构造器，只负责生成标准图层配置，不会直接操作地图。构造结果可以放入 `config.basemap.list`，也可以传给 `setBasemap()`。\n\n```js\nimport { BaseGIS } from '@3clear/basegis'\nimport {\n  createGeoserverPyramidLayer,\n  createTiandituLayer,\n  createWmsLayer,\n  createWmtsLayer,\n} from '@3clear/basegis/layers'\n\nconst basemapList = [\n  createTiandituLayer({\n    id: 'tianditu-imagery',\n    name: '天地图影像',\n    category: 'basemap',\n    resourceKey: 'imagery',\n    annotationResourceKey: 'imageryLabel',\n  }),\n  createGeoserverPyramidLayer({\n    id: 'geoserver-pyramid',\n    name: 'GeoServer 金字塔瓦片',\n    category: 'basemap',\n    url: 'https://example.com/tiles/{z}/{x}/{y}.png',\n  }),\n  createWmsLayer({\n    id: 'weather-wms',\n    name: '气象 WMS',\n    category: 'basemap',\n    serviceUrl: 'https://example.com/geoserver/wms',\n    layers: 'workspace:weather',\n    parameters: {\n      transparent: true,\n      format: 'image/png',\n    },\n  }),\n  createWmtsLayer({\n    id: 'weather-wmts',\n    name: '气象 WMTS',\n    category: 'basemap',\n    engineSupport: ['cesium'],\n    url: 'https://example.com/geoserver/gwc/service/wmts?' +\n      'SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=workspace:weather&' +\n      'STYLE=default&TILEMATRIXSET=EPSG:3857&FORMAT=image/png&' +\n      'TILEMATRIX=EPSG:3857:{z}&TILEROW={y}&TILECOL={x}',\n  }),\n]\n\nconst mapCore = new BaseGIS({\n  containerId: 'map',\n  config: {\n    basemap: {\n      list: basemapList,\n      defaultVisibleId: 'tianditu-imagery',\n    },\n  },\n})\n```\n\n四个构造器都会保留额外传入字段，便于继续配置 `engineSupport`、缩放级别、注记资源或服务参数。\n\n`createWmtsLayer()` 只生成配置，不会根据 `layer / tileMatrixSet` 自动拼接请求。自定义 WMTS 需要完整的 GetTile URL 模板，矩阵标识应按服务实际配置填写；XYZ 瓦片同样使用包含 `{z}/{x}/{y}` 的 URL。\n\n模板瓦片不会自动重投影。上面的 `EPSG:3857` WMTS 示例限定用于 Cesium；当前 Leaflet 使用 `EPSG:4326`，自定义瓦片需匹配地图的坐标系和瓦片矩阵。\n\n`layers` 还导出辅助函数 `createTiandituTileSource(resourceKey = 'imagery')`，返回 `{ url, subdomains }`，资源不可用时返回 `null`。它供需要直接操作引擎的特殊页面使用，普通页面仍优先调用 `createTiandituLayer()` 和 `BaseGIS`。\n\n<a id=\"base-gis\"></a>\n\n## BaseGIS 基础能力\n\n这一节列的是 `BaseGIS` 主入口直接提供的基础能力。业务页面优先调用这些方法；图片图层、网格图层、海量点、点位聚合、点位抽稀、等值线、风场等更复杂能力，建议使用后文 `methods` 中对应的 Controller。\n\n<a id=\"base-gis-lifecycle\"></a>\n\n### 1. 生命周期与实例\n\n`BaseGIS` 负责地图实例的初始化、状态读取、尺寸刷新和销毁。构造参数、完整配置项、参数优先级及 `setConfig()` 的生效规则见[地图初始化与运行时配置](#map-init-config)。\n\n```js\nconst mapCore = new BaseGIS({\n  engineType: 'cesium',\n  containerId: 'map',\n})\n\nconst initResult = mapCore.init()\nif (!initResult.success) {\n  console.warn(initResult.message)\n}\n\nconst engineType = mapCore.getEngineType()\nconst config = mapCore.getConfig() // 只读使用，不要直接修改。\nconst mapInstance = mapCore.getMapInstance()\n\nmapCore.resize()\nmapCore.destroy()\n```\n\n方法说明：\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `init(options)` | `{ container, containerId, engineType, sceneMode, initialView }` | 初始化地图。重复调用会重建地图并异步恢复 BaseGIS 托管图层。 |\n| `whenReady()` | 无 | 等待最近一次 `init()` 触发的托管图层恢复完成。 |\n| `destroy()` | 无 | 销毁当前地图实例和 adapter，并清空托管图层快照。 |\n| `setConfig(config)` | `Object` | 合并实例配置，无返回值且不自动重建地图；[查看完整规则](#map-init-config)。 |\n| `getEngineType()` | 无 | 返回当前引擎类型。 |\n| `getConfig()` | 无 | 返回当前配置对象引用，建议只读。 |\n| `getMapInstance()` | 无 | 返回底层地图实例：Cesium `viewer` 或 Leaflet `map`。 |\n| `getMapContainer()` | 无 | 返回统一结果，`data.container` 为地图容器 DOM。 |\n| `resize()` | 无 | 在容器尺寸改变、隐藏面板重新显示后刷新地图尺寸。 |\n\n<a id=\"engine-switch\"></a>\n\n### 2. 切换引擎\n\n`BaseGIS` 支持 `cesium` 和 `leaflet` 两种引擎。调用 `setEngine()` 可以在运行时切换引擎、保留当前视野，并恢复 BaseGIS 托管图层。\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `setEngine(engineType, options)` | 引擎类型、初始化参数 | 未初始化时记录默认引擎；已初始化时自动切换、保留视野并恢复托管图层。 |\n| `switchEngine(engineType, options)` | 引擎类型、初始化参数 | `setEngine()` 的兼容别名。 |\n\n目标引擎与当前引擎相同时，`setEngine()` 会直接返回，不会应用 `options.sceneMode / initialView / container`。同一引擎下调整场景或视角应调用 `setSceneMode()`、`setInitialView()`；需要重建地图时直接调用 `init()`。\n\n发生实际切换时，`setEngine()` 会把目标场景模式，以及显式传入或从当前地图保留的视角写回 `config.view`，供之后再次初始化使用。\n\n#### 推荐写法\n\n页面上做 Cesium / Leaflet 切换时，直接调用 `setEngine()`。它会保留当前视野、重建目标引擎，并等待托管图层自动恢复。\n\n```js\nasync function changeEngine(engineType) {\n  if (!mapCore || mapCore.getEngineType() === engineType) {\n    return\n  }\n\n  const result = await mapCore.setEngine(engineType)\n\n  if (!result.success) {\n    console.warn(result.message)\n    return\n  }\n}\n```\n\n直接重复调用 `init({ engineType })` 也会自动开始恢复；如果后续逻辑依赖恢复完成，需要再等待 `whenReady()`：\n\n```js\nconst result = mapCore.init({\n  engineType: 'leaflet',\n  containerId: 'map',\n})\n\nif (result.success) {\n  await mapCore.whenReady()\n}\n```\n\n`switchEngine()` 作为兼容别名保留，行为与 `setEngine()` 一致。\n\n<a id=\"managed-layer-restore\"></a>\n\n#### 切换后的图层处理\n\n引擎切换不是把 Cesium 图层对象“搬到” Leaflet，也不是把 Leaflet 图层对象“搬到” Cesium。BaseGIS 会保存托管图层的业务参数，并在新 adapter 中重新创建图层。\n\n- 自动恢复范围包括图片、网格、海量点、点位聚合、点位抽稀、独立线、源解析传输、台风路径、等值线、统一风场和三维体图层。\n- 图层最新的数据参数、显隐、清空、删除、海量点删除和高亮状态会同步到 BaseGIS 快照。\n- 快照只保存业务参数引用，不复制大数组，不保存任何底层引擎对象。\n- 三维体、源解析传输切到 Leaflet 时会返回不支持结果，但快照仍保留，切回 Cesium 后会继续恢复。\n- Cesium 专有能力在 Leaflet 下不可用，例如 DEM、三维体渲染、三维切片/剖面渲染。\n- 基础绘制对象、GeoJSON、点击/视图监听、GPU 专用风场、独立 `WindFieldMethods`、剖面图层和页面直接创建的底层引擎对象不在托管范围内，需要业务重新加载或绑定。\n\n切换结果中可以查看恢复明细：\n\n```js\nconst result = await mapCore.setEngine('leaflet')\nconsole.log(result.data?.restore?.restored)\nconsole.log(result.data?.restore?.failed)\n```\n\n`result.success` 表示引擎初始化/切换是否成功，不保证每个图层都恢复成功；应同时检查 `data.restore.failed`。不要在切换前先调用 `destroy()`，它会清空托管快照。\n\n<a id=\"view-control-scene\"></a>\n\n### 3. 视角控制与场景模式\n\n视角控制分为缩放、重置视角、设置初始视角、场景模式切换、视图状态读取和视图变化监听。\n\n```js\n// 放大 / 缩小。\n// Cesium 可传 { distance }，Leaflet 可传 { step }。\nmapCore.zoomIn()\nmapCore.zoomOut()\nmapCore.zoomIn({ distance: 300000 })\nmapCore.zoomOut({ step: 1 })\n\n// 回到配置中的 initialView，也可以传入目标视角覆盖。\nmapCore.resetView()\nmapCore.resetView({\n  center: [104, 35],\n  height: 3000000,\n  zoom: 5,\n  pitch: -90,\n})\n\n// 设置视角。center / position 均为 [经度, 纬度]。\nmapCore.setInitialView({\n  center: [104, 35],\n  // Cesium 使用 height。\n  height: 3000000,\n  // Leaflet 使用 zoom。\n  zoom: 5,\n  // Cesium 使用 heading / pitch / roll，单位是度。\n  heading: 0,\n  pitch: -90,\n  roll: 0,\n})\n\n// 场景模式。Cesium 支持 2d / 2.5d / 3d；Leaflet 只支持 2d。\nmapCore.setSceneMode('3d')\nmapCore.setSceneMode({\n  mode: '2d',\n  // Cesium morph 动画时长，单位秒。\n  duration: 0.4,\n  // Cesium 场景切换后是否尽量恢复原视野，默认 true。\n  preserveView: true,\n})\nmapCore.getSceneMode()\n\n// 获取当前视图边界。\nconst boundsResult = mapCore.getViewBounds()\n// boundsResult.data: { west, south, east, north }\n\n// 获取当前视图状态。\nconst viewStateResult = mapCore.getViewState()\n// Cesium 通常包含 center / bounds / height / heading / pitch / roll / engineType。\n// Leaflet 通常包含 center / bounds / zoom / engineType。\n\n// 让地图适配指定经纬度范围，可用于多地图首次统一范围。\nmapCore.fitViewBounds({\n  bounds: { west: 73, south: 18, east: 135, north: 54 },\n  animate: false,\n  padding: 0,\n})\n\n// 经纬度转地图容器像素坐标，常用于自定义 HTML 浮层定位。\nconst pointResult = mapCore.projectToContainerPoint({\n  longitude: 104,\n  latitude: 35,\n  height: 0,\n})\n// pointResult.data: { x, y }\n```\n\n<a id=\"map-view-listener\"></a>\n\n视图变化监听：\n\n```js\nconst viewListener = mapCore.onViewChange({\n  // Cesium: camera.moveStart / morphStart；Leaflet: movestart / zoomstart。\n  onStart() {},\n\n  // Cesium: camera.changed；Leaflet: move / zoom / resize。\n  // 缩放过程中要实时刷新点位样式时，优先用 onChange。\n  onChange() {},\n\n  // Cesium 交互期间逐渲染帧检测相机变化；适合多地图实时联动。\n  // 默认 false，普通业务监听无需开启。\n  continuous: true,\n\n  // Cesium: camera.moveEnd / morphComplete；Leaflet: moveend / zoomend / resize。\n  onEnd() {},\n\n  // Cesium onEnd 延迟，默认 120ms。\n  endDelay: 120,\n})\n\n// 组件卸载时移除监听。\nviewListener.data?.off?.()\n```\n\n方法说明：\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `zoomIn(payload)` | Cesium `{ distance }`；Leaflet `{ step }` | 放大地图。 |\n| `zoomOut(payload)` | Cesium `{ distance }`；Leaflet `{ step }` | 缩小地图。 |\n| `resetView(payload)` | 视角对象，可选 | 回到初始视角或传入的目标视角。 |\n| `setInitialView(payload)` | `{ center, height, zoom, heading, pitch, roll }` | 设置当前视角。 |\n| `setSceneMode(payload)` | `'2d'/'2.5d'/'3d'` 或 `{ mode, duration, preserveView }` | 切换场景模式。 |\n| `getSceneMode()` | 无 | 获取当前场景模式。 |\n| `getViewBounds()` | 无 | 获取当前视图经纬度边界。 |\n| `getViewState(payload)` | 可选 `{includeBounds:false}` | 获取当前视图状态；实时同引擎联动可关闭较重的范围采样。 |\n| `fitViewBounds(payload)` | `{bounds:{west,south,east,north},animate?,padding?,duration?}` | 适配指定经纬度范围；`duration` 仅用于 Cesium 动画。 |\n| `onViewChange(payload)` | `{ onStart, onChange, onEnd, endDelay, continuous? }` | 监听视图变化，返回 `{ off }`；Cesium 开启 `continuous` 后交互期间逐帧检测。 |\n| `projectToContainerPoint(payload)` | `{ longitude, latitude, height }` | 经纬度投影到地图容器像素坐标。 |\n\n<a id=\"map-screenshot\"></a>\n\n### 4. 地图截图与导出\n\n`screenshot()` 用于导出当前 Cesium 或 Leaflet 地图，两个引擎使用完全相同的调用方式、参数和结果对象。默认截取当前可视范围（地图视口）并下载 PNG，不会自动拼接视口外尚未渲染的地图内容；只截取地图内容，不包含页面工具栏、弹窗等地图容器外的 DOM 浮层。\n\n框选遮罩样式包含在 `@3clear/basegis/style.css` 中，使用 npm 包时应按安装章节导入该样式文件。\n\n在线示例：[地图截图与 PNG 导出](http://61.50.111.214:31166/lgmap/test-page-40)。\n\n#### 直接下载 PNG\n\n默认 `download: true`，页面不需要自己创建下载链接或处理 Blob。`fileName` 可指定文件名；不传时使用前缀和时间戳自动命名。\n\n```js\n// 截取当前可视范围并直接下载。\nconst result = await mapCore.screenshot({ fileName: 'weather-map.png' })\nif (!result.success) {\n  console.warn('截图导出失败', result.code, result.message)\n}\n\n// 左键拖拽框选区域，松开后下载；右键或 Esc 取消。\nconst selectResult = await mapCore.screenshot({\n  mode: 'select',\n  namePrefix: 'map-select',\n})\nif (!selectResult.success && selectResult.code !== 'CANCELLED') {\n  console.warn('框选导出失败', selectResult.message)\n}\n```\n\n#### 按范围裁剪导出\n\n```js\n// 按当前视野中的经纬度范围裁剪。\nawait mapCore.screenshot({\n  namePrefix: 'east-china',\n  bounds: { west: 115, south: 25, east: 123, north: 36 },\n})\n\n// 按相对当前地图视口左上角的 CSS 像素区域裁剪。\nawait mapCore.screenshot({\n  rect: { left: 120, top: 80, width: 640, height: 360 },\n})\n```\n\n#### 获取 Blob，不自动下载\n\n```js\nconst captureResult = await mapCore.screenshot({\n  download: false,\n  fileName: 'weather-map.png',\n})\nif (captureResult.success) {\n  const { blob, fileName, width, height } = captureResult.data\n  // 页面可将 blob 交给自己的预览或上传逻辑，BaseGIS 不会自动上传。\n  console.log(blob, fileName, width, height)\n}\n```\n\n#### 方法与参数\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `screenshot(payload)` | `{ mode?, download?, fileName?, namePrefix?, bounds?, area?, rect?, screenRect? }` | 截取 Cesium / Leaflet 地图；`mode: 'select'` 进入交互框选，`download` 默认为 `true`。 |\n| `cancelScreenshotSelection(reason?)` | 可选取消原因字符串 | 主动取消正在进行的框选；返回布尔值，表示是否取消了现有框选。 |\n\n| 参数 | 默认值 | 说明 |\n| --- | --- | --- |\n| `mode` | 省略 | 默认直接截图；`'select'` 进入鼠标框选，也可简写为 `screenshot('select')`。 |\n| `download` | `true` | 自动触发浏览器下载；传 `false` 只生成截图结果。 |\n| `fileName` | 自动生成 | 优先于 `namePrefix`；未以 `.png` 结尾时自动追加扩展名。当前仅导出 PNG。 |\n| `namePrefix` | `map-screenshot` | 自动文件名前缀；未指定名称的框选截图默认使用 `map-select`。 |\n| `bounds` / `area` | 当前视口 | 经纬度范围别名，支持 `{ west, south, east, north }` 或 `{ startLon, startLat, endLon, endLat }`。 |\n| `rect` / `screenRect` | 当前视口 | 屏幕区域别名，支持 `{ left, top, width, height }`，也可用 `x / y` 代替 `left / top`；单位为相对地图视口左上角的 CSS 像素。 |\n\n取消框选时，原 `screenshot()` Promise 返回 `success: false`、`code: 'CANCELLED'` 和 `data.cancelled: true`；页面通常无需按错误弹窗处理。\n\n成功结果的 `data` 包含 `blob`、`width`、`height`、`mimeType`、`fileName`、`downloaded`、`screenRect` 和 `pixelRect`。Cesium 读取 WebGL 场景画布；Leaflet 在首次调用时按需加载 DOM 渲染器，合成瓦片、SVG、Canvas 和 DOM Marker，并排除 Leaflet 自带控件。跨域地图资源需要服务端允许 CORS，否则对应资源可能缺失或截图返回 `FAILED`。为保证 WebGL 截图可靠，Cesium 初始化时会保留 drawing buffer；在超高分辨率大屏上会增加一定显存占用。\n\n`width / height / pixelRect` 使用导出图片的实际像素，可能与 CSS 像素不同；`downloaded: true` 仅表示已触发下载，不代表浏览器已确认文件保存到磁盘。导出范围始终限制在当前视口内，不是整页长截图，也不支持自动加载并拼接屏幕外瓦片。\n\n<a id=\"basic-graphics\"></a>\n\n### 5. 绘制点、线、面、文字和 Marker\n\n这些方法用于轻量绘制和样例验证。大量点位请优先使用后文的 `PointLargeLayerController`、`PointClusterController`、`PointDensityController` 等控制器。\n\n点：\n\n```js\nmapCore.drawPoint({\n  id: 'point-1',\n  name: '点位',\n  // 必填建议：[经度, 纬度]。\n  position: [104, 35],\n  height: 0,\n  // Cesium 使用 pixelSize；Leaflet 使用 radius。\n  pixelSize: 10,\n  radius: 7,\n  color: '#ff4d4f',\n})\n```\n\n线：\n\n```js\nmapCore.drawLine({\n  id: 'line-1',\n  name: '连线',\n  positions: [\n    [103, 34],\n    [105, 36],\n  ],\n  width: 3,\n  color: '#1677ff',\n})\n```\n\n面：\n\n```js\nmapCore.drawPolygon({\n  id: 'polygon-1',\n  name: '区域',\n  positions: [\n    [102, 33],\n    [106, 33],\n    [106, 36],\n    [102, 36],\n  ],\n  // Cesium 使用 fillColor；Leaflet 主要使用 color / fillOpacity。\n  fillColor: 'rgba(22, 119, 255, 0.25)',\n  color: '#1677ff',\n  outlineWidth: 2,\n  fillOpacity: 0.35,\n  // Cesium 默认贴地；显式传 height 时按非贴地面绘制。\n  height: 0,\n})\n```\n\n文字：\n\n```js\nmapCore.drawText({\n  id: 'text-1',\n  name: '文字',\n  position: [104, 35],\n  text: '示例文字',\n  font: '16px Microsoft YaHei',\n  color: '#0f2d4d',\n  backgroundColor: 'rgba(255,255,255,0.7)',\n  // 默认 center/bottom，也可以传 left/top/right/bottom。\n  textAnchor: {\n    horizontal: 'center',\n    vertical: 'bottom',\n  },\n  textOffset: [0, 0],\n})\n```\n\n图片 Marker：\n\n```js\nmapCore.addMarker({\n  id: 'marker-1',\n  name: '站点',\n  position: [104, 35],\n  iconUrl: '/marker.png',\n  iconSize: [32, 32],\n  // 也支持 iconWidth / iconHeight。\n  iconAnchor: [16, 32],\n  iconOffset: [0, 0],\n  label: '站点名称',\n})\n```\n\n<a id=\"marker-icons\"></a>\n\n#### Canvas 数据图标与扩散效果\n\n`mapCore.createMarkerIcon(options, data)` 支持圆点、“数值 + 名称”、气象风向、“图片 + 名称”和“图片 + 数值 + 名称”五种 Canvas 样式。圆点用法：\n\n```js\nconst station = {\n  id: 'station-1',\n  name: '示例站点',\n  position: [104, 35],\n  value: 28.6,\n  status: 'normal',\n  windDirection: 90,\n  windSpeed: 6,\n}\n\nconst markerIcon = mapCore.createMarkerIcon({\n  type: 'dot',\n  size: 18,\n  color: '#64748b',\n  getColor(data) {\n    if (data.status === 'normal') return '#00e600'\n    if (data.status === 'warning') return '#a67824'\n    return '#6b7280'\n  },\n}, station)\n\nmapCore.addMarker({\n  id: station.id,\n  name: station.name,\n  position: station.position,\n  data: station,\n  ...markerIcon,\n})\n```\n\n“数值 + 名称”用法：\n\n```js\nconst markerIcon = mapCore.createMarkerIcon({\n  type: 'value-label',\n  value: station.value,\n  label: station.name,\n  showLabel: station.showName !== false,\n  color: '#64748b',\n  getColor(data) {\n    if (data.status === 'normal') return '#00e600'\n    if (data.status === 'warning') return '#a67824'\n    return '#6b7280'\n  },\n}, station)\n\nmapCore.addMarker({\n  id: station.id,\n  name: station.name,\n  position: station.position,\n  data: station,\n  ...markerIcon,\n})\n```\n\n气象风向用法：\n\n```js\nconst markerIcon = mapCore.createMarkerIcon({\n  type: 'wind',\n  direction: station.windDirection,\n  label: station.name,\n  showLabel: station.showName !== false,\n  color: '#64748b',\n  getColor(data) {\n    if (data.windSpeed >= 17.2) return '#e5484d'\n    if (data.windSpeed >= 10.8) return '#f59e0b'\n    if (data.windSpeed >= 5.5) return '#2f80ed'\n    return '#00e600'\n  },\n}, station)\n\nmapCore.addMarker({\n  id: station.id,\n  position: station.position,\n  data: station,\n  ...markerIcon,\n})\n```\n\n“图片 + 名称”用法：\n\n```js\nimport { gisInfoMarker } from '@3clear/basegis/assets'\n\nconst stationImage = new Image()\n// 跨域图片需由服务端允许 CORS，并在设置 src 前配置 crossOrigin。\nstationImage.src = gisInfoMarker\nawait stationImage.decode()\n\nconst markerIcon = mapCore.createMarkerIcon({\n  type: 'image-label',\n  image: stationImage,\n  imageSize: [20, 20],\n  label: station.name,\n  showLabel: station.showName !== false,\n}, station)\n\nmapCore.addMarker({\n  id: station.id,\n  position: station.position,\n  data: station,\n  ...markerIcon,\n})\n```\n\n“图片 + 数值 + 名称”用法：\n\n```js\nimport { gisFactoryMarker } from '@3clear/basegis/assets'\n\nconst company = { id: 'factory-1', name: '示例企业', emissionValue: 12.5 }\nconst factoryImage = new Image()\nfactoryImage.src = gisFactoryMarker\nawait factoryImage.decode()\n\nconst markerIcon = mapCore.createMarkerIcon({\n  type: 'image-value-label',\n  image: factoryImage,\n  imageSize: [14, 14],\n  value: company.emissionValue,\n  label: company.name,\n  showValue: company.showValue !== false,\n  showLabel: company.showName !== false,\n}, company)\n\nmapCore.addMarker({\n  id: company.id,\n  position: [104.2, 35.1],\n  data: company,\n  ...markerIcon,\n})\n```\n\n`getColor(data)` 同步接收 `createMarkerIcon` 第二个参数传入的原始数据并返回 CSS 颜色。回调异常、返回非法颜色或空值时使用 `options.color`，再无有效静态颜色时使用默认灰色。圆点 `size` 默认 18px，允许 4～96px，并使用中心锚点。`value-label` 的 `value` 位于上方彩色色块，`label` 位于下方白色名称块；`showLabel: false` 时只显示数值块，图标宽高和锚点会自动更新。`wind` 的 `direction` 使用角度，`0` 指向正上方（北）、`90` 指向右侧（东），按顺时针方向增加；`label` 位于下方白色名称块，`showLabel: false` 时可隐藏，风速颜色分级完全由业务回调决定。`image-label` 的 `image` 接收已加载的 CanvasImageSource，`imageSize` 控制图片尺寸；URL 图片应先加载一次再传入，以保持抽稀回调同步。`image-value-label` 在同一规则上增加右侧 `value` 和蓝色圆角色块；`showValue: false` 时不绘制数值底板，只保留图片本身，`showLabel: false` 可独立隐藏名称。两者都隐藏时得到纯图片图标，画布尺寸和锚点会按照 `imageSize` 自动收缩。该类型使用整数锚点减少低 DPR 下的文字模糊。返回的 `iconUrl/iconSize/iconAnchor` 可用于普通 Marker、点位抽稀及其他图片点位能力。\n\nCesium / Leaflet 双引擎扩散 Marker：\n\n```js\nmapCore.addMarker({\n  id: 'alarm-station',\n  name: '告警站点',\n  position: [121.48, 31.23],\n  data: station,\n  iconUrl: '/marker.png',\n  iconSize: [34, 40],\n  pulse: {\n    color: '#22d3ee',\n    size: 88,\n    duration: 2,\n    ringCount: 2,\n    ringWidth: 2,\n    ringOpacity: 0.72,\n  },\n  clampToGround: true,\n  onClick({ data }) {\n    console.log(data)\n  },\n})\n```\n\n`pulse` 只是图标下方的效果层，不会生成中心点。`pulse.size` 是最大扩散直径（像素），`ringWidth` 是独立的屏幕像素线宽，`ringOpacity` 是圆环不透明度；`duration` 是单个圆环的周期（秒），`ringCount` 支持 1～3。页面不需要判断当前引擎；Leaflet 使用 SVG 动画，Cesium 使用 Billboard 缩放动画，两端保持相同的半径、线宽、透明度和相位变化曲线。只传 `pulse` 时仅显示扩散环；与 `iconUrl` 同时传入时，图标显示在扩散环上层。\n\n<a id=\"gis-marker-sample\"></a>\n\n#### 使用 SVG 资源\n\n`assets` 导出的资源均为可直接用于 `iconUrl` 的 SVG URL；传给 Canvas 图标的 `image` 时则需要先加载为图片对象。\n\n| 导出 | 用途 |\n| --- | --- |\n| `gisMarkerSample` | 基础 Marker 示例。 |\n| `gisFactoryMarker` | 工厂标记，适合企业排放数据。 |\n| `gisInfoMarker` | 信息标记，适合站点名称与数值。 |\n| `typhoonPathIcon` | 台风路径默认中心图标。 |\n\n```js\nimport { gisMarkerSample } from '@3clear/basegis/assets'\n\nmapCore.addMarker({\n  id: 'marker-sample',\n  name: '示例站点',\n  position: [104, 35],\n  iconUrl: gisMarkerSample,\n  iconSize: [42, 50],\n  iconAnchor: [21, 50],\n})\n```\n\n清理图形：\n\n```js\nmapCore.removeGraphic({ id: 'marker-1' })\nmapCore.removeGraphic('marker-1')\nmapCore.clearGraphics()\n```\n\n方法说明：\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `drawPoint(payload)` | 点配置 | 绘制点。 |\n| `drawLine(payload)` | 线配置 | 绘制线。 |\n| `drawPolygon(payload)` | 面配置 | 绘制面。 |\n| `drawText(payload)` | 文字配置 | 绘制文字。 |\n| `createMarkerIcon(options, data)` | 图标配置、原始数据 | 直接返回 `iconUrl/iconSize/iconAnchor`；未知 `type` 返回 `null`。 |\n| `addMarker(payload)` | Marker 配置 | 绘制图片或 `pulse` 扩散 Marker；未传图片或扩散参数时，Cesium 使用点、Leaflet 使用默认 Marker。 |\n| `removeGraphic(payload)` | 图形 id 或 `{ id }` | 删除指定图形。 |\n| `clearGraphics()` | 无 | 清空通过基础绘制方法创建的图形。 |\n\n\n\n<a id=\"geojson-layer\"></a>\n\n### 6. GeoJSON 图层\n\n`upsertGeoJsonLayer()` 用同一个 `layerId` 创建或替换 GeoJSON 点、线、面图层，两个引擎共用数据与样式参数。图层基础样式使用 `style`，按要素覆盖的完整样式由 `styleCallback(feature, index)` 返回。坐标按 GeoJSON 顺序传入 `[经度, 纬度, 可选高度]`。\n\n```js\nconst result = await mapCore.upsertGeoJsonLayer({\n  layerId: 'region-boundaries',\n  data: {\n    type: 'FeatureCollection',\n    features: [{\n      type: 'Feature',\n      id: 'region-1',\n      properties: {\n        name: '示例区域',\n      },\n      geometry: {\n        type: 'Polygon',\n        coordinates: [[[103, 34], [105, 34], [105, 36], [103, 36], [103, 34]]],\n      },\n    }],\n  },\n  styleCallback(feature) {\n    return {\n      color: '#36d3ff',\n      weight: 3,\n      opacity: 1,\n      dashArray: '8 4',\n      fillColor: '#1677ff',\n      fillOpacity: 0.4,\n    }\n  },\n  onClick({ feature, properties }) {\n    console.log(properties.name, feature)\n  },\n})\n\nif (result.success && result.data.bounds) {\n  mapCore.fitViewBounds({ bounds: result.data.bounds, animate: false })\n}\n```\n\n| 参数 | 说明 |\n| --- | --- |\n| `layerId` | 必填，图层唯一 id。 |\n| `data` | 必填；支持 FeatureCollection、Feature、Geometry、Feature 数组、JSON 字符串或 URL。也兼容 `geojson / geoJson / source / url`。 |\n| `fetchOptions` | 使用 URL 时传给 Fetch 的请求参数。业务接口通常由宿主 `api/modules` 获取后再传入 `data`。 |\n| `style` | 统一样式：`color / weight / opacity / dashArray / fillColor / fillOpacity / stroke / fill`；Leaflet 圆点大小用 `radius`，Cesium 标记大小用 `markerSize`。 |\n| `styleCallback(feature, index)` | 按数据返回样式覆盖；线、面支持全部 `style` 字段，点还支持 `radius / markerSize`；异常时使用基础样式。 |\n| `onClick(event)` | 返回 `layerId / feature / properties / data / event / target / engineType`；`data` 为原始 Feature。 |\n| `visible` | 默认 `true`。 |\n| `clampToGround` | Cesium 是否贴地，默认 `true`。 |\n\n```js\nmapCore.hideGeoJsonLayer({ layerId: 'region-boundaries' })\nmapCore.showGeoJsonLayer({ layerId: 'region-boundaries' })\nconst stateResult = mapCore.getGeoJsonLayerState({ layerId: 'region-boundaries' })\nconsole.log(stateResult.data) // layerId / layerType / visible / featureCount / bounds\n\nmapCore.clearGeoJsonLayer({ layerId: 'region-boundaries' }) // 清空要素，保留图层。\nmapCore.removeGeoJsonLayer({ layerId: 'region-boundaries' }) // 删除图层。\n```\n\n`styleCallback` 可直接返回完整样式；需要按要素变化时，在回调中读取 `feature.properties` 后决定返回值即可。`opacity: 0`、`fillOpacity: 0`、`stroke: false`、`fill: false` 都会按原值保留。两个引擎都会渲染虚线，但 `dashArray` 的具体节奏是近似效果。\n\n上述管理方法也可直接传 id 字符串；方法名兼容大写 `GeoJSON`，例如 `upsertGeoJSONLayer()`。更新时需重新提供完整数据和样式。GeoJSON 当前不在自动恢复清单中，切换引擎后需重新调用 `upsertGeoJsonLayer()`。\n\n<a id=\"dem-terrain\"></a>\n\n### 7. DEM 地形\n\n仅 Cesium 支持。默认使用 `ellipsoid-flat` 椭球地形；`tianditu-terrain` 是底图瓦片，不会提供高程。当前 DEM 接口支持椭球和 Cesium World Terrain，配置中的 `tianditu-dem` 仍是占位项，不能作为已实现地形源使用。\n\n```js\n// 使用 World Terrain 前，宿主需准备 Cesium Ion 等所需凭据与网络访问。\nconst result = mapCore.loadDEMById('cesium-world-terrain')\nif (!result.success) {\n  console.warn(result.message)\n} else if (result.data.loading) {\n  // 某些 Cesium 版本异步加载地形；调用返回成功不等于地形已经就绪。\n  const provider = await result.data.readyPromise\n  if (!provider) console.warn('World Terrain 加载失败')\n}\n\nmapCore.setTerrainExaggeration({ factor: 2 })\nconsole.log(mapCore.getDEMState().data)\n```\n\n| 方法 | 说明 |\n| --- | --- |\n| `loadDEMById(id)` | 从 `config.dem.list` 查找地形配置，常用 `ellipsoid-flat / cesium-world-terrain`。 |\n| `loadDEM(payload)` | 直接传配置；支持 `{ id, sourceType: 'ellipsoid-flat' }` 或 `{ id, sourceType: 'cesium-official', factory: 'worldTerrain' }`。 |\n| `loadDefaultDEM()` | 按当前 `config.dem` 加载默认地形。 |\n| `getDEMState()` | `data` 返回 `{ id, enabled, exaggeration }`；`enabled` 按 `id !== 'ellipsoid-flat'` 判断，不代表异步地形已就绪。 |\n| `setTerrainExaggeration(payload)` | 传数字或 `{ factor }`，范围 `1～10`。 |\n\n恢复平面地形可调用 `mapCore.loadDEMById('ellipsoid-flat')`。Leaflet 不提供 DEM；调用失败时应检查 `success / code / message`，不要直接假定结果包含地形状态。\n\n<a id=\"map-click-events\"></a>\n\n### 8. 点击事件\n\n`onClick` 注册地图点击事件；`offClick` 移除当前点击监听。当前每个 adapter 只保留一个基础点击监听，重复调用 `onClick` 会先移除旧监听。\n\n```js\nmapCore.onClick({\n  callback(event) {\n    console.log(event)\n  },\n})\n\nmapCore.offClick()\n```\n\nCesium 点击空地时的事件结构：\n\n```js\n{\n  clickType: 'coordinate',\n  engineType: 'cesium',\n  coordinates: {\n    longitude: 104,\n    latitude: 35,\n    height: 0,\n  },\n}\n```\n\nCesium 点击 Entity 时的事件结构：\n\n```js\n{\n  clickType: 'entity',\n  engineType: 'cesium',\n  coordinates: {\n    longitude: 104,\n    latitude: 35,\n    height: 0,\n  },\n  target: {\n    id: 'point-1',\n    name: '点位',\n    entityType: 'entity',\n  },\n}\n```\n\nLeaflet 当前返回坐标点击：\n\n```js\n{\n  clickType: 'coordinate',\n  engineType: 'leaflet',\n  coordinates: {\n    longitude: 104,\n    latitude: 35,\n    height: 0,\n  },\n}\n```\n\n说明：\n\n- 基础 `onClick` 适合地图空白点击、简单 Entity 点击。\n- 海量点、点位聚合、点位抽稀等图层自己的点击事件，应使用对应 Controller 的点击回调参数。\n\n\n<a id=\"map-view-link-controller\"></a>\n\n## 多地图联动（双屏联动）\n\n`MapViewLinkController` 注册多个已经初始化的 BaseGIS，并实时同步拖动、缩放和视角。每个地图仍可独立加载不同模式、时次和图层；控制器只负责视角，不创建地图，也不会在 `unregisterMap()`、`clear()` 或 `destroy()` 时销毁 BaseGIS。\n\n在线示例：[查看分屏布局中的页面地图联动](http://61.50.111.214:31166/lgmap/test-page-36)。布局可混合地图、图表和表格，地图实例与联动由页面管理。\n\n```js\nimport { BaseGIS } from '@3clear/basegis'\nimport { MapViewLinkController } from '@3clear/basegis/methods'\n\nconst mapA = new BaseGIS({\n  engineType: 'leaflet',\n  containerId: 'map-a',\n})\nconst mapB = new BaseGIS({\n  engineType: 'leaflet',\n  containerId: 'map-b',\n})\nmapA.init()\nmapB.init()\n\nconst viewLinks = new MapViewLinkController({\n  enabled: true,\n  realtime: true,\n  mode: 'all',       // all | leader | group\n  strategy: 'auto',  // auto | view | bounds\n  leaderId: 'map-a',\n  syncInterval: 32, // 实时同步间隔，约 30fps\n})\n\nviewLinks.registerMap('map-a', mapA, { group: 'forecast' })\nviewLinks.registerMap('map-b', mapB, { group: 'forecast' })\n\n// 获取 BaseGIS、底层地图实例和全部 id -> BaseGIS 映射。\nconst registeredMapA = viewLinks.getMap('map-a')\nconst leafletMapA = viewLinks.getNativeMap('map-a')\nconst allMaps = viewLinks.getMaps()\n\n// 以 map-a 为操作源放大，并实时同步其他地图。\nviewLinks.zoomIn({ mapId: 'map-a', step: 1 })\n\n// 程序化控制全部注册地图。\nviewLinks.setView({ center: [104, 34], zoom: 5, height: 5000000 })\nviewLinks.fitBounds({ bounds: { west: 73, south: 18, east: 135, north: 54 } })\n\n// 页面卸载：控制器先解绑，地图仍由页面自己销毁。\nviewLinks.destroy()\nmapA.destroy()\nmapB.destroy()\n```\n\n构造参数：\n\n| 参数 | 类型 | 默认值 | 说明 |\n| --- | --- | --- | --- |\n| `enabled` | `Boolean` | `true` | 是否开启联动。 |\n| `realtime` | `Boolean` | `true` | `true` 按动画帧同步移动/缩放；`false` 只在操作结束时同步。 |\n| `mode` | `String` | `'all'` | `all` 任意图控制；`leader` 仅主图；`group` 仅同组地图。 |\n| `strategy` | `String` | `'auto'` | `auto` 自动选择；`view` 优先中心和层级/高度；`bounds` 使用视图范围。 |\n| `leaderId` | `String` | `''` | 主图 id；空值会使用首个注册地图。 |\n| `syncInterval` | `Number` | `32` | 实时同步的最小间隔，单位 ms；默认约 30fps，降低多 Cesium 实例同时渲染的压力。 |\n| `suppressionMs` | `Number` | `220` | 被同步地图的事件抑制时长，避免反向循环。 |\n| `endDelay` | `Number` | `80` | Cesium 操作结束事件延迟，单位 ms。 |\n| `onStateChange` | `Function` | `null` | 注册、模式、主图和操作结束后的回调，参数为 `{ reason, state }`。 |\n\n联动与实例方法：\n\n| 方法 | 参数 | 返回 / 说明 |\n| --- | --- | --- |\n| `registerMap(id,mapCore,options)` | `options:{group?,enabled?,replace?}` | 注册已初始化的 BaseGIS 并绑定实时视图监听。 |\n| `unregisterMap(id)` | 地图 id | 解除监听和注册，不销毁 BaseGIS。 |\n| `refreshMap(id)` | 地图 id | `BaseGIS.setEngine()` 后重新绑定新 adapter 的监听。 |\n| `getMap(id)` | 地图 id | 返回 BaseGIS 或 `null`。 |\n| `getNativeMap(id)` | 地图 id | 返回 Cesium Viewer / Leaflet Map 或 `null`。 |\n| `getMaps()` | 无 | 返回新的 `Map<id, BaseGIS>`，修改它不会改变控制器注册表。 |\n| `getMapIds()` / `hasMap(id)` | 可选 id | 查询注册状态。 |\n| `forEachMap(callback)` | `(mapCore,id,entry)` | 遍历已注册 BaseGIS。 |\n| `getLeaderMap()` / `getActiveMap()` | 无 | 获取主图或最近操作地图。 |\n| `setLeader(id)` | 地图 id | 设置主图。 |\n| `setMode(mode)` | `all/leader/group` | 更新联动模式。 |\n| `setStrategy(strategy)` | `auto/view/bounds` | 更新视角映射策略。 |\n| `setMapGroup(id,group)` | 地图 id、组名 | 更新地图所在联动组。 |\n| `setMapEnabled(id,enabled)` | 地图 id、布尔值 | 单独启停某张地图的联动。 |\n| `setEnabled(enabled)` / `pause()` / `resume()` | 可选布尔值 | 整体启停联动。 |\n| `setRealtime(realtime)` | 布尔值 | 切换实时或操作结束后同步。 |\n| `syncFrom(id,options)` | 地图 id、`force/strategy` 等 | 立即把指定地图视角同步给目标地图。 |\n\n统一视角操作：\n\n| 方法 | 参数 | 说明 |\n| --- | --- | --- |\n| `zoomIn(payload)` | `{mapId?,step?,distance?}` | 按显式 `mapId`、最近活动地图、主图的顺序选取操作源，再按联动规则同步。 |\n| `zoomOut(payload)` | 同上 | 缩小并同步。 |\n| `setView(payload)` | `center,zoom,height,heading,pitch,roll` | 设置全部注册地图视角。 |\n| `fitBounds(payload)` | `{bounds,animate?,padding?,duration?}` | 让全部地图适配相同范围。 |\n| `resetView(payload)` | 可选视角 | 重置全部地图。 |\n| `resizeAll(payload)` | 可选参数 | 刷新全部地图容器尺寸。 |\n| `getState()` | 无 | 返回模式、主图、注册地图、最近同步来源等状态。 |\n| `clear()` / `destroy()` | 无 | 解绑全部监听并释放引用，不销毁 BaseGIS。 |\n\n`setView / fitBounds / resetView / resizeAll` 始终作用于所有注册地图，不受联动分组或暂停状态限制。\n\n同为 Leaflet 时会同步 `center + zoom`；同为 Cesium 时会直接同步相机经纬度、`height + heading/pitch/roll`；两种视图模型无法直接对应时，`auto` 才会计算并使用 `bounds`。Cesium 交互期间会逐帧检测相机变化，再按 `syncInterval` 合并为最新状态写入目标地图，避免目标地图追赶稀疏跳点，也避免四五个 Cesium 实例每帧重复计算完整视域。一次拖动期间会锁定唯一交互源，目标地图的程序化相机事件不会反向接管并形成反馈循环。\n\n> BaseGIS 切换引擎会重建 adapter，原视图监听随旧 adapter 销毁。切换完成后调用 `viewLinks.refreshMap(id)`；控制器不会劫持或改写 `BaseGIS.setEngine()`。\n\n\n<a id=\"line-layer-controller\"></a>\n\n## LineLayerController\n\n独立管理一组折线，支持整体更新、显隐、清空、销毁和引擎切换恢复。每条线必须提供唯一 `id` 和至少两个 `[longitude, latitude, height?]` 坐标点；同一控制器同一时刻只播放一条线的逐步出线动画。\n\n### 逐顶点色专题线与时间裁剪\n\n`lines[].colors` 是六位 HEX 数组，长度必须与 `positions` 完全一致，颜色沿每两个相邻顶点连续插值，优先于 `style.color`：\n\n```js\nimport { LineLayerController } from '@3clear/basegis/methods'\n\nconst lineLayer = new LineLayerController({ mapCore, layerId: 'sampling-tracks' })\nconst startTime = Date.parse('2026-08-28T08:00:00+08:00')\nawait lineLayer.load({\n  lines: [{\n    id: 'sampling-track',\n    positions: [[113.30, 22.60], [113.31, 22.61]],\n    colors: ['#00FF00', '#FF3B30'],\n    times: [startTime, startTime + 60000],\n  }],\n  style: { width: 5, opacity: 1, clampToGround: false },\n  animation: { enabled: false },\n  currentTime: startTime,\n})\n\nlineLayer.setTime(startTime + 30000) // 显示前 30 秒已走过的路线。\nlineLayer.setTime(null) // 恢复完整路线。\n```\n\n需要行驶回放时，为各条线附加 `times: [起点毫秒时间戳, 终点毫秒时间戳, ...]`，长度与坐标相同且严格递增。`load` 可设置 `currentTime`，加载后调用 `lineLayer.setTime(time)` 裁掉未来部分；`setTime(null)` 恢复完整路线。不带 `times` 的线不参与裁剪。成功返回 `{ success: true, data: { layerId, currentTime } }`，非法时间返回友好失败结果。\n\nCesium 使用一个批量 Primitive 做顶点色插值，时间用相对秒数存入顶点，回放每帧仅改材质参数。Leaflet 使用共享 Canvas 重绘已走过的线段，不重新投影或创建图层。这是实线专题叠加，不能与虚线、流动、单线 reveal 或贴地混用；Cesium 关闭深度测试，带时间的采样按直线连接，不用于地形遮挡/贴合，位置必须不同。数据/配色更新仍为全量替换，显隐和引擎切换保留时间。浓度映射、分车/断线、时钟和小车属于业务层；参考项目 `/test-page-43`。\n\n### 实线、虚线与纯色\n\n```js\nimport { LineLayerController } from '@3clear/basegis/methods'\n\nconst lineLayer = new LineLayerController({\n  mapCore,\n  layerId: 'flight-route',\n  style: {\n    color: '#00d8ff',\n    width: 4,\n    opacity: 0.9,\n    pattern: 'dashed', // solid | dashed\n    dashLength: 14,\n    gapLength: 8,\n  },\n})\n\nawait lineLayer.load({\n  lines: [{\n    id: 'route-1',\n    positions: [\n      [116.4, 39.9],\n      [117.8, 37.5],\n      [121.5, 31.2],\n    ],\n  }],\n})\n```\n\n单条线的 `style` 可以覆盖控制器的顶层默认样式。\n\n### 固定渐变与流动渐变\n\n`style.color` 可以是 CSS 颜色字符串，也可以是渐变配置。`stops` 的 `offset` 按 `0～1` 升序排列；配置 `flow` 后渐变会沿线路循环流动。\n\n```js\nconst gradientStyle = {\n  width: 5,\n  pattern: 'solid',\n  color: {\n    type: 'gradient',\n    stops: [\n      { offset: 0, color: '#00e5ff' },\n      { offset: 0.5, color: '#2563eb' },\n      { offset: 1, color: '#a855f7' },\n    ],\n    flow: {\n      enabled: true,\n      durationMs: 2400,\n      direction: 'forward', // forward | reverse\n    },\n  },\n}\n```\n\n渐变与虚线可以同时配置。Cesium 和 Leaflet 的底层绘制机制不同，虚线端点和颜色交界处可能有轻微视觉差异。\n\n### 飞机引领的逐步出线\n\n飞机不是单独的类，而是通用 `animation.icon` 配置。省略 `icon` 时只播放线路逐步出现。\n\n```js\n// BaseGIS 不内置飞机图片，替换成业务项目自己的资源地址。\nconst planeIconUrl = '/your-app/plane.svg'\n\nconst flightLine = new LineLayerController({\n  mapCore,\n  layerId: 'flight-route',\n  style: gradientStyle,\n  animation: {\n    enabled: true,\n    lineId: 'route-1',\n    mode: 'reveal',\n    durationMs: 12000,\n    autoplay: true,\n    loop: true,\n    icon: {\n      url: planeIconUrl,\n      size: [36, 36],\n      rotateToPath: true,\n      rotationOffsetDeg: 0,\n    },\n  },\n})\n\nawait flightLine.load({\n  lines: [{\n    id: 'route-1',\n    positions: [\n      [116.4, 39.9],\n      [117.8, 37.5],\n      [121.5, 31.2],\n    ],\n  }],\n})\n\nflightLine.pauseAnimation()\nflightLine.playAnimation()\nflightLine.restartAnimation()\n```\n\n动画按照各段实际地理距离插值。`hide()` 暂停帧更新并记录播放状态，`show()` 恢复隐藏前正在运行的动画；`clear()` 和 `destroy()` 会取消动画。切换引擎会恢复播放/暂停意图，但不会保存逐帧进度，动画从起点重新计算。\n\n### 参数\n\n| 参数 | 默认值 | 说明 |\n| --- | --- | --- |\n| `layerId` | `line-default` | 独立线图层 id。 |\n| `lines` | `[]` | 折线数组，每项包含唯一 `id` 和至少两个 `positions` 坐标。 |\n| `lines[].colors` | - | 与坐标等长的六位 HEX 数组，优先于 `style.color`；用于实线专题叠加。 |\n| `lines[].times` | - | 与坐标等长、严格递增的有限毫秒时间戳；必须与 `colors` 配合。 |\n| `currentTime` | `null` | 路线时间裁剪位置；`null` 显示全部。 |\n| `visible` | `true` | 初始是否可见。 |\n| `style.color` | `#2f80ff` | 纯色字符串或渐变对象。 |\n| `style.width` / `style.opacity` | `3` / `1` | 线宽和透明度。 |\n| `style.pattern` | `solid` | `solid` 或 `dashed`。 |\n| `style.dashLength` / `gapLength` | `12` / `8` | 虚线实段和间隔长度。 |\n| `animation.enabled` | `false` | 是否启用逐步出线。 |\n| `animation.lineId` | 单线时自动选择 | 多条线启用动画时必填。 |\n| `animation.durationMs` | `10000` | 从起点到终点的时长。 |\n| `animation.autoplay` / `loop` | `true` / `false` | 自动播放与循环。 |\n| `animation.icon` | - | 可选的飞机、车辆或船舶图标配置。 |\n\n### 方法\n\n| 方法 | 说明 |\n| --- | --- |\n| `mount(mapCore)` | 后挂载 BaseGIS。 |\n| `load(payload, options?)` / `update(payload)` | 加载或更新线、样式和动画。 |\n| `setTime(currentTime)` | 更新路线时间裁剪；传 `null` 恢复完整路线，不重建全部线数据。 |\n| `show()` / `hide()` / `toggle()` | 控制显隐。 |\n| `playAnimation()` / `pauseAnimation()` / `restartAnimation()` | 控制沿线动画。 |\n| `clear()` | 清空线和动画，保留控制器。 |\n| `refreshState()` / `getState()` | 获取线数量、点数和动画状态。 |\n| `destroy()` | 删除图层、动画和引擎恢复快照。 |\n\n页面卸载时先执行 `lineLayer.destroy()`，再执行 `mapCore.destroy()`。\n\n在线示例：[查看 LineLayerController 双引擎应用 Demo](http://61.50.111.214:31166/lgmap/test-page-34)。\n\n<a id=\"typhoon-path-controller\"></a>\n\n## TyphoonPathController\n\n把已经整理好的台风实况和预报数据渲染为独立专题图层，统一支持 Cesium / Leaflet。控制器负责实况路径、强度着色节点、生命周期标签、移动中心、当前点 7/10/12 级四象限风圈、多机构预报、不确定性圆和播放定位。\n\nBaseGIS 不请求台风接口，也不推算强度、风圈、预报或预警等级。分页筛选、接口字段转换、ECharts 和业务面板仍由应用层负责。24/48 小时警戒线属于普通业务折线，推荐与 `LineLayerController` 组合使用。\n\n### 最小用法\n\n```js\nimport { TyphoonPathController } from '@3clear/","readmeFilename":"README.md"}