@caoguo/maplibre 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,7 +20,7 @@ function buildTiandituSources(type, opts) {
20
20
  const { base, label } = LAYER_MAP[type];
21
21
  const tileSize = opts.tileSize ?? 256;
22
22
  const maxzoom = opts.maxzoom ?? 18;
23
- const attr = "\xA9 \u5929\u5730\u56FE GS(2023)3295\u53F7";
23
+ const attr = "\xA9 \u5929\u5730\u56FE GS(2023)3295\u53F7 \xB7 \u8349\u679C\u5730\u56FE";
24
24
  return [
25
25
  {
26
26
  id: `tianditu-${base}`,
@@ -92,5 +92,5 @@ function addTiandituBaseMap(opts) {
92
92
  }
93
93
 
94
94
  export { MissingTokenError, addTiandituBaseMap, buildTiandituSources, tiandituStyle, tiandituTileUrls };
95
- //# sourceMappingURL=chunk-EZOV5O2M.js.map
96
- //# sourceMappingURL=chunk-EZOV5O2M.js.map
95
+ //# sourceMappingURL=chunk-QGVXYR2I.js.map
96
+ //# sourceMappingURL=chunk-QGVXYR2I.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sources/tianditu.ts","../src/sources/addTianditu.ts"],"names":[],"mappings":"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","file":"chunk-EZOV5O2M.js","sourcesContent":["/**\n * 天地图(Tianditu)WMTS 底图源(F-1.2)。\n *\n * 天地图为国内权威底图,坐标系为 CGCS2000 / Web Mercator(显示尺度等价于 WGS84)。\n * token 必须由调用方在运行时注入,**绝不硬编码**;缺失时抛出明确错误。\n *\n * 文档:https://lbs.tianditu.gov.cn/server/MapService.html\n */\n\nexport type TiandituLayer =\n /** 矢量底图(含道路/注记基础) */\n | 'vec'\n /** 影像底图 */\n | 'img'\n /** 地形底图 */\n | 'ter'\n /** 矢量注记(道路/地名) */\n | 'cva'\n /** 影像注记 */\n | 'cia'\n /** 地形注记 */\n | 'cta'\n /** 矢量注记(英文) */\n | 'eva'\n /** 影像注记(英文) */\n | 'eia'\n /** 地形注记(英文) */\n | 'eta';\n\nexport type TiandituType = 'vector' | 'satellite' | 'terrain';\n\nexport interface TiandituOptions {\n /** 天地图授权 token(必填,运行时注入) */\n token: string;\n /** 语言:中文 cva/cia/cta 或英文 eva/eia/eta */\n lang?: 'zh' | 'en';\n /** 瓦片服务子域,默认 0-7 */\n subdomains?: number[];\n /** 瓦片尺寸,默认 256 */\n tileSize?: number;\n /** 最大层级,默认 18 */\n maxzoom?: number;\n}\n\nconst LAYER_MAP: Record<TiandituType, { base: TiandituLayer; label: TiandituLayer }> = {\n vector: { base: 'vec', label: 'cva' },\n satellite: { base: 'img', label: 'cia' },\n terrain: { base: 'ter', label: 'cta' },\n};\n\nconst LAYER_LABEL_EN: Partial<Record<TiandituLayer, TiandituLayer>> = {\n cva: 'eva',\n cia: 'eia',\n cta: 'eta',\n};\n\n/** 构建单张 WMTS 栅格源的 tiles URL 列表(含子域与 token) */\nexport function tiandituTileUrls(\n layer: TiandituLayer,\n opts: TiandituOptions\n): string[] {\n const sub = opts.subdomains ?? [0, 1, 2, 3, 4, 5, 6, 7];\n const labelLayer = opts.lang === 'en' ? LAYER_LABEL_EN[layer] ?? layer : layer;\n return sub.map(\n (s) =>\n `https://t${s}.tianditu.gov.cn/${labelLayer}_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${labelLayer}&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${encodeURIComponent(opts.token)}`\n );\n}\n\nexport interface TiandituSourceSpec {\n id: string;\n source: {\n type: 'raster';\n tiles: string[];\n tileSize: number;\n maxzoom: number;\n attribution: string;\n };\n /** 是否注记层(绘制于底图之上) */\n isLabel: boolean;\n}\n\n/** 生成一组天地图源(底图 + 注记) */\nexport function buildTiandituSources(\n type: TiandituType,\n opts: TiandituOptions\n): TiandituSourceSpec[] {\n const { base, label } = LAYER_MAP[type];\n const tileSize = opts.tileSize ?? 256;\n const maxzoom = opts.maxzoom ?? 18;\n const attr = '© 天地图 GS(2023)3295号';\n return [\n {\n id: `tianditu-${base}`,\n isLabel: false,\n source: {\n type: 'raster',\n tiles: tiandituTileUrls(base, opts),\n tileSize,\n maxzoom,\n attribution: attr,\n },\n },\n {\n id: `tianditu-${label}`,\n isLabel: true,\n source: {\n type: 'raster',\n tiles: tiandituTileUrls(label, opts),\n tileSize,\n maxzoom,\n attribution: attr,\n },\n },\n ];\n}\n\nexport class MissingTokenError extends Error {\n constructor() {\n super('天地图 token 缺失:调用 addTiandituBaseMap 必须传入 tianditu: { token } 选项。');\n this.name = 'MissingTokenError';\n }\n}\n","/**\n * 把天地图底图(底图 + 注记)作为源/图层添加到 MapLibre 地图实例。\n *\n * 由于天地图为栅格底图,推荐作为「无 style 背景」的地图底层:\n * 直接通过 setStyle 注入最小 style,或先 addSource + addLayer。\n * 这里提供低侵入的「注入已有 map」方式,业务可叠加自有矢量图层。\n */\n\nimport type { Map as MlMap, StyleSpecification } from 'maplibre-gl';\nimport {\n buildTiandituSources,\n MissingTokenError,\n type TiandituOptions,\n type TiandituType,\n} from './tianditu';\n\nexport interface AddTiandituOptions extends TiandituOptions {\n type?: TiandituType;\n /** 添加到指定地图实例 */\n map: MlMap;\n /** 注入为 style 的源/图层还是 addSource(默认 addSource + addLayer) */\n asStyle?: false;\n}\n\n/** 生成可直接作为 Map style 的天地图 Style JSON(含底图+注记两层) */\nexport function tiandituStyle(\n type: TiandituType,\n opts: TiandituOptions\n): StyleSpecification {\n if (!opts.token) throw new MissingTokenError();\n const sources = buildTiandituSources(type, opts);\n return {\n version: 8,\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',\n sources: Object.fromEntries(\n sources.map((s) => [s.id, s.source])\n ) as StyleSpecification['sources'],\n layers: sources.map((s) => ({\n id: s.id,\n type: 'raster' as const,\n source: s.id,\n // 注记层置于底图之上\n ...(s.isLabel ? { minzoom: 0 } : {}),\n })),\n };\n}\n\n/** 向已存在的地图实例注入天地图底图(底图 + 注记) */\nexport function addTiandituBaseMap(opts: AddTiandituOptions): void {\n if (!opts.token) throw new MissingTokenError();\n const type = opts.type ?? 'vector';\n const sources = buildTiandituSources(type, opts);\n const map = opts.map;\n for (const s of sources) {\n if (!map.getSource(s.id)) {\n map.addSource(s.id, s.source as never);\n }\n if (!map.getLayer(s.id)) {\n map.addLayer({\n id: s.id,\n type: 'raster',\n source: s.id,\n } as never);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/sources/tianditu.ts","../src/sources/addTianditu.ts"],"names":[],"mappings":"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","file":"chunk-QGVXYR2I.js","sourcesContent":["/**\n * 天地图(Tianditu)WMTS 底图源(F-1.2)。\n *\n * 天地图为国内权威底图,坐标系为 CGCS2000 / Web Mercator(显示尺度等价于 WGS84)。\n * token 必须由调用方在运行时注入,**绝不硬编码**;缺失时抛出明确错误。\n *\n * 文档:https://lbs.tianditu.gov.cn/server/MapService.html\n */\n\nexport type TiandituLayer =\n /** 矢量底图(含道路/注记基础) */\n | 'vec'\n /** 影像底图 */\n | 'img'\n /** 地形底图 */\n | 'ter'\n /** 矢量注记(道路/地名) */\n | 'cva'\n /** 影像注记 */\n | 'cia'\n /** 地形注记 */\n | 'cta'\n /** 矢量注记(英文) */\n | 'eva'\n /** 影像注记(英文) */\n | 'eia'\n /** 地形注记(英文) */\n | 'eta';\n\nexport type TiandituType = 'vector' | 'satellite' | 'terrain';\n\nexport interface TiandituOptions {\n /** 天地图授权 token(必填,运行时注入) */\n token: string;\n /** 语言:中文 cva/cia/cta 或英文 eva/eia/eta */\n lang?: 'zh' | 'en';\n /** 瓦片服务子域,默认 0-7 */\n subdomains?: number[];\n /** 瓦片尺寸,默认 256 */\n tileSize?: number;\n /** 最大层级,默认 18 */\n maxzoom?: number;\n}\n\nconst LAYER_MAP: Record<TiandituType, { base: TiandituLayer; label: TiandituLayer }> = {\n vector: { base: 'vec', label: 'cva' },\n satellite: { base: 'img', label: 'cia' },\n terrain: { base: 'ter', label: 'cta' },\n};\n\nconst LAYER_LABEL_EN: Partial<Record<TiandituLayer, TiandituLayer>> = {\n cva: 'eva',\n cia: 'eia',\n cta: 'eta',\n};\n\n/** 构建单张 WMTS 栅格源的 tiles URL 列表(含子域与 token) */\nexport function tiandituTileUrls(\n layer: TiandituLayer,\n opts: TiandituOptions\n): string[] {\n const sub = opts.subdomains ?? [0, 1, 2, 3, 4, 5, 6, 7];\n const labelLayer = opts.lang === 'en' ? LAYER_LABEL_EN[layer] ?? layer : layer;\n return sub.map(\n (s) =>\n `https://t${s}.tianditu.gov.cn/${labelLayer}_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${labelLayer}&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${encodeURIComponent(opts.token)}`\n );\n}\n\nexport interface TiandituSourceSpec {\n id: string;\n source: {\n type: 'raster';\n tiles: string[];\n tileSize: number;\n maxzoom: number;\n attribution: string;\n };\n /** 是否注记层(绘制于底图之上) */\n isLabel: boolean;\n}\n\n/** 生成一组天地图源(底图 + 注记) */\nexport function buildTiandituSources(\n type: TiandituType,\n opts: TiandituOptions\n): TiandituSourceSpec[] {\n const { base, label } = LAYER_MAP[type];\n const tileSize = opts.tileSize ?? 256;\n const maxzoom = opts.maxzoom ?? 18;\n const attr = '© 天地图 GS(2023)3295号 · 草果地图';\n return [\n {\n id: `tianditu-${base}`,\n isLabel: false,\n source: {\n type: 'raster',\n tiles: tiandituTileUrls(base, opts),\n tileSize,\n maxzoom,\n attribution: attr,\n },\n },\n {\n id: `tianditu-${label}`,\n isLabel: true,\n source: {\n type: 'raster',\n tiles: tiandituTileUrls(label, opts),\n tileSize,\n maxzoom,\n attribution: attr,\n },\n },\n ];\n}\n\nexport class MissingTokenError extends Error {\n constructor() {\n super('天地图 token 缺失:调用 addTiandituBaseMap 必须传入 tianditu: { token } 选项。');\n this.name = 'MissingTokenError';\n }\n}\n","/**\n * 把天地图底图(底图 + 注记)作为源/图层添加到 MapLibre 地图实例。\n *\n * 由于天地图为栅格底图,推荐作为「无 style 背景」的地图底层:\n * 直接通过 setStyle 注入最小 style,或先 addSource + addLayer。\n * 这里提供低侵入的「注入已有 map」方式,业务可叠加自有矢量图层。\n */\n\nimport type { Map as MlMap, StyleSpecification } from 'maplibre-gl';\nimport {\n buildTiandituSources,\n MissingTokenError,\n type TiandituOptions,\n type TiandituType,\n} from './tianditu';\n\nexport interface AddTiandituOptions extends TiandituOptions {\n type?: TiandituType;\n /** 添加到指定地图实例 */\n map: MlMap;\n /** 注入为 style 的源/图层还是 addSource(默认 addSource + addLayer) */\n asStyle?: false;\n}\n\n/** 生成可直接作为 Map style 的天地图 Style JSON(含底图+注记两层) */\nexport function tiandituStyle(\n type: TiandituType,\n opts: TiandituOptions\n): StyleSpecification {\n if (!opts.token) throw new MissingTokenError();\n const sources = buildTiandituSources(type, opts);\n return {\n version: 8,\n glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',\n sources: Object.fromEntries(\n sources.map((s) => [s.id, s.source])\n ) as StyleSpecification['sources'],\n layers: sources.map((s) => ({\n id: s.id,\n type: 'raster' as const,\n source: s.id,\n // 注记层置于底图之上\n ...(s.isLabel ? { minzoom: 0 } : {}),\n })),\n };\n}\n\n/** 向已存在的地图实例注入天地图底图(底图 + 注记) */\nexport function addTiandituBaseMap(opts: AddTiandituOptions): void {\n if (!opts.token) throw new MissingTokenError();\n const type = opts.type ?? 'vector';\n const sources = buildTiandituSources(type, opts);\n const map = opts.map;\n for (const s of sources) {\n if (!map.getSource(s.id)) {\n map.addSource(s.id, s.source as never);\n }\n if (!map.getLayer(s.id)) {\n map.addLayer({\n id: s.id,\n type: 'raster',\n source: s.id,\n } as never);\n }\n }\n}\n"]}
package/dist/index.cjs CHANGED
@@ -197,7 +197,7 @@ function buildTiandituSources(type, opts) {
197
197
  const { base, label } = LAYER_MAP[type];
198
198
  const tileSize = opts.tileSize ?? 256;
199
199
  const maxzoom = opts.maxzoom ?? 18;
200
- const attr = "\xA9 \u5929\u5730\u56FE GS(2023)3295\u53F7";
200
+ const attr = "\xA9 \u5929\u5730\u56FE GS(2023)3295\u53F7 \xB7 \u8349\u679C\u5730\u56FE";
201
201
  return [
202
202
  {
203
203
  id: `tianditu-${base}`,
@@ -765,15 +765,96 @@ var ThemeSwitcher = class {
765
765
  }
766
766
  };
767
767
 
768
+ // src/controls/LegendControl.ts
769
+ function escapeHtml(s) {
770
+ return s.replace(
771
+ /[&<>"']/g,
772
+ (c) => c === "&" ? "&amp;" : c === "<" ? "&lt;" : c === ">" ? "&gt;" : c === '"' ? "&quot;" : "&#39;"
773
+ );
774
+ }
775
+ function renderLegendHtml(items, title) {
776
+ const titleHtml = title ? `<div class="cg-legend-title">${escapeHtml(title)}</div>` : "";
777
+ const itemsHtml = items.map((it) => {
778
+ const swatch = it.shape === "line" ? `<span class="cg-legend-swatch cg-legend-swatch--line" style="background:${it.color}"></span>` : `<span class="cg-legend-swatch" style="background:${it.color}"></span>`;
779
+ return `<div class="cg-legend-item">${swatch}<span>${escapeHtml(it.label)}</span></div>`;
780
+ }).join("");
781
+ return `<div class="cg-legend-body">${titleHtml}${itemsHtml}</div>`;
782
+ }
783
+ var LegendControl = class {
784
+ constructor(options) {
785
+ this.opts = { title: options.title ?? "", items: options.items };
786
+ this.el = options.container ?? document.createElement("div");
787
+ this.el.className = "caoguo-legend-control";
788
+ this.el.style.cssText = "position:absolute;right:10px;top:10px;padding:8px 10px;background:rgba(10,15,30,.72);color:#cfe;border-radius:6px;font:12px/1.5 system-ui,sans-serif;z-index:2;max-width:220px;";
789
+ this.bodyEl = document.createElement("div");
790
+ this.el.appendChild(this.bodyEl);
791
+ this.render();
792
+ }
793
+ render() {
794
+ this.bodyEl.innerHTML = renderLegendHtml(this.opts.items, this.opts.title || void 0);
795
+ }
796
+ /** 运行时更新图例项(随图层切换重新渲染) */
797
+ setItems(items, title) {
798
+ if (title !== void 0) this.opts.title = title;
799
+ this.opts.items = items;
800
+ this.render();
801
+ }
802
+ /** 挂载到容器 */
803
+ addTo(container) {
804
+ if (!this.el.parentElement) container.appendChild(this.el);
805
+ return this;
806
+ }
807
+ remove() {
808
+ this.el.remove();
809
+ }
810
+ };
811
+
812
+ // src/controls/ExportControl.ts
813
+ function triggerDownload(dataUrl, filename) {
814
+ const a = document.createElement("a");
815
+ a.href = dataUrl;
816
+ a.download = filename.endsWith(".png") ? filename : `${filename}.png`;
817
+ document.body.appendChild(a);
818
+ a.click();
819
+ a.remove();
820
+ }
821
+ var ExportControl = class {
822
+ constructor(map, options = {}) {
823
+ this.onClick = () => this.export();
824
+ this.map = map;
825
+ this.filename = options.filename ?? "caoguo-map";
826
+ this.el = options.container ?? document.createElement("button");
827
+ this.el.className = "caoguo-export-control";
828
+ this.el.textContent = options.buttonText ?? "\u5BFC\u51FA PNG";
829
+ this.el.style.cssText = "position:absolute;right:10px;top:10px;padding:5px 10px;background:rgba(10,15,30,.72);color:#cfe;border:1px solid #2a3550;border-radius:4px;font:12px system-ui,sans-serif;cursor:pointer;z-index:3;";
830
+ this.el.addEventListener("click", this.onClick);
831
+ }
832
+ /** 导出当前地图视图为 PNG 并下载 */
833
+ export() {
834
+ const canvas = this.map.getCanvas();
835
+ const url = canvas.toDataURL("image/png");
836
+ triggerDownload(url, this.filename);
837
+ }
838
+ /** 挂载到容器 */
839
+ addTo(container) {
840
+ if (!this.el.parentElement) container.appendChild(this.el);
841
+ return this;
842
+ }
843
+ remove() {
844
+ this.el.removeEventListener("click", this.onClick);
845
+ this.el.remove();
846
+ }
847
+ };
848
+
768
849
  // src/shaders/glowGeometry.ts
769
- function glowPasses(passes = 4, coreOpacity = 1) {
850
+ function glowPasses(passes = 4, baseWidth = 3) {
770
851
  if (passes < 1) passes = 1;
771
852
  const out = [];
772
853
  for (let i = 0; i < passes; i++) {
773
854
  const t = passes === 1 ? 1 : 1 - i / (passes - 1);
774
855
  out.push({
775
- widthScale: 1 + (1 - t) * (passes - 1) * 1.5,
776
- opacity: coreOpacity * (0.12 + 0.88 * t)
856
+ width: baseWidth * (1 + (1 - t) * (passes - 1) * 1.8),
857
+ opacity: 0.1 + 0.9 * t * t
777
858
  });
778
859
  }
779
860
  return out;
@@ -781,17 +862,40 @@ function glowPasses(passes = 4, coreOpacity = 1) {
781
862
  function projectSimple(lng, lat) {
782
863
  const x = lng / 180;
783
864
  const y = Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)) / Math.PI;
784
- return [x, Math.max(-1, Math.min(1, y))];
865
+ return [x, Math.max(-1, Math.min(1, y / Math.PI))];
785
866
  }
786
867
  function buildGlowGeometry(lines, opts = {}) {
787
- const passes = glowPasses(opts.passes ?? 4, opts.coreOpacity ?? 1);
788
- let vertexCount = 0;
789
- const projected = lines.map((l) => {
790
- const points = l.coordinates.map(([lng, lat]) => projectSimple(lng, lat));
791
- vertexCount += Math.max(0, points.length - 1) * 2;
792
- return { points, group: l.group };
793
- });
794
- return { lines: projected, passes, vertexCount };
868
+ const passes = glowPasses(opts.passes ?? 4, opts.baseWidth ?? 3);
869
+ const stride = 5;
870
+ const verts = [];
871
+ const passRanges = [];
872
+ for (const pass of passes) {
873
+ const start = verts.length / stride;
874
+ for (const line of lines) {
875
+ const pts = line.coordinates.map(([lng, lat]) => projectSimple(lng, lat));
876
+ if (pts.length < 2) continue;
877
+ for (let i = 0; i < pts.length - 1; i++) {
878
+ const a = pts[i];
879
+ const b = pts[i + 1];
880
+ const dx = b[0] - a[0];
881
+ const dy = b[1] - a[1];
882
+ const quad = [
883
+ [a[0], a[1], dx, dy, 1],
884
+ [a[0], a[1], dx, dy, -1],
885
+ [b[0], b[1], dx, dy, 1],
886
+ [b[0], b[1], dx, dy, -1]
887
+ ];
888
+ const idx = [0, 1, 2, 2, 1, 3];
889
+ for (const k of idx) {
890
+ const v = quad[k];
891
+ verts.push(v[0], v[1], v[2], v[3], v[4]);
892
+ }
893
+ }
894
+ }
895
+ const count = verts.length / stride - start;
896
+ passRanges.push({ start, count });
897
+ }
898
+ return { vertices: new Float32Array(verts), passRanges, passes, stride };
795
899
  }
796
900
 
797
901
  // src/shaders/CustomLineLayer.ts
@@ -805,17 +909,21 @@ var DEFAULT_COLORS = {
805
909
  };
806
910
  var VERT = `
807
911
  attribute vec2 aPos;
808
- attribute float aMiter;
912
+ attribute vec2 aDir;
913
+ attribute float aSide;
809
914
  uniform mat4 uMatrix;
810
915
  uniform float uWidth;
811
916
  uniform vec2 uResolution;
812
917
  void main() {
813
- // \u7B80\u5316\uFF1A\u6CBF\u6CD5\u7EBF\u65B9\u5411\u504F\u79FB width\uFF08\u5C4F\u5E55\u7A7A\u95F4\uFF09\uFF0C\u8FD9\u91CC\u4EC5\u6F14\u793A\u6838\u5FC3\u6295\u5F71
814
918
  vec4 clip = uMatrix * vec4(aPos, 0.0, 1.0);
815
- vec2 screen = clip.xy / clip.w;
816
- // \u6CE8\uFF1A\u7CBE\u786E\u7EBF\u5BBD\u9700\u6CD5\u7EBF\u8BA1\u7B97\uFF0C\u793A\u8303\u5C42\u7528\u70B9\u5BBD\u8FD1\u4F3C
817
- vec2 offset = vec2(aMiter * uWidth / uResolution.x * 2.0, 0.0);
818
- gl_Position = vec4(screen + offset * clip.w, clip.z, clip.w);
919
+ // \u6BB5\u65B9\u5411\u4E5F\u6295\u5F71\uFF0C\u6C42\u5C4F\u5E55\u7A7A\u95F4\u65B9\u5411
920
+ vec4 clipDir = uMatrix * vec4(aPos + aDir, 0.0, 1.0);
921
+ vec2 s0 = clip.xy / clip.w;
922
+ vec2 s1 = clipDir.xy / clipDir.w;
923
+ vec2 dir = normalize(s1 - s0 + vec2(1e-6));
924
+ vec2 normal = vec2(-dir.y, dir.x);
925
+ vec2 offset = normal * aSide * (uWidth / uResolution * 2.0);
926
+ gl_Position = vec4(s0 + offset * clip.w, clip.z, clip.w);
819
927
  }
820
928
  `;
821
929
  var FRAG = `
@@ -830,6 +938,9 @@ function compile(gl, type, src) {
830
938
  const sh = gl.createShader(type);
831
939
  gl.shaderSource(sh, src);
832
940
  gl.compileShader(sh);
941
+ if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
942
+ console.error("[CustomLineLayer] shader \u7F16\u8BD1\u5931\u8D25:", gl.getShaderInfoLog(sh));
943
+ }
833
944
  return sh;
834
945
  }
835
946
  var CustomLineLayer = class {
@@ -840,8 +951,7 @@ var CustomLineLayer = class {
840
951
  this.lines = opts.lines;
841
952
  this.colors = { ...DEFAULT_COLORS, ...opts.colors };
842
953
  this.baseWidth = opts.baseWidth ?? 3;
843
- this.passes = opts.passes ?? 4;
844
- this.geometry = buildGlowGeometry(this.lines, { passes: this.passes });
954
+ this.geometry = buildGlowGeometry(this.lines, { passes: opts.passes ?? 4, baseWidth: this.baseWidth });
845
955
  }
846
956
  onAdd(map, gl) {
847
957
  this.map = map;
@@ -851,29 +961,29 @@ var CustomLineLayer = class {
851
961
  gl.attachShader(prog, vs);
852
962
  gl.attachShader(prog, fs);
853
963
  gl.linkProgram(prog);
854
- this.program = prog;
855
- const verts = [];
856
- for (const line of this.geometry.lines) {
857
- const pts = line.points;
858
- for (let i = 0; i < pts.length - 1; i++) {
859
- verts.push(pts[i][0], pts[i][1], 0, pts[i + 1][0], pts[i + 1][1], 0);
860
- }
964
+ if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
965
+ console.error("[CustomLineLayer] program \u94FE\u63A5\u5931\u8D25:", gl.getProgramInfoLog(prog));
861
966
  }
967
+ this.program = prog;
862
968
  const buf = gl.createBuffer();
863
969
  gl.bindBuffer(gl.ARRAY_BUFFER, buf);
864
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);
970
+ gl.bufferData(gl.ARRAY_BUFFER, this.geometry.vertices, gl.STATIC_DRAW);
865
971
  this.buffer = buf;
866
972
  }
867
973
  render(gl, matrix) {
868
974
  if (!this.program || !this.buffer) return;
869
975
  gl.useProgram(this.program);
870
976
  gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
977
+ const stride = this.geometry.stride * 4;
871
978
  const aPos = gl.getAttribLocation(this.program, "aPos");
872
- const aMiter = gl.getAttribLocation(this.program, "aMiter");
979
+ const aDir = gl.getAttribLocation(this.program, "aDir");
980
+ const aSide = gl.getAttribLocation(this.program, "aSide");
873
981
  gl.enableVertexAttribArray(aPos);
874
- gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 24, 0);
875
- gl.enableVertexAttribArray(aMiter);
876
- gl.vertexAttribPointer(aMiter, 1, gl.FLOAT, false, 24, 16);
982
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, stride, 0);
983
+ gl.enableVertexAttribArray(aDir);
984
+ gl.vertexAttribPointer(aDir, 2, gl.FLOAT, false, stride, 8);
985
+ gl.enableVertexAttribArray(aSide);
986
+ gl.vertexAttribPointer(aSide, 1, gl.FLOAT, false, stride, 16);
877
987
  const uMatrix = gl.getUniformLocation(this.program, "uMatrix");
878
988
  gl.uniformMatrix4fv(uMatrix, false, new Float32Array(matrix));
879
989
  const uWidth = gl.getUniformLocation(this.program, "uWidth");
@@ -885,11 +995,14 @@ var CustomLineLayer = class {
885
995
  gl.enable(gl.BLEND);
886
996
  gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
887
997
  const color = this.colors[this.lines[0]?.group ?? "pipe"] ?? this.colors.pipe;
888
- for (const pass of this.geometry.passes) {
889
- gl.uniform1f(uWidth, this.baseWidth * pass.widthScale);
998
+ for (let p = 0; p < this.geometry.passes.length; p++) {
999
+ const pass = this.geometry.passes[p];
1000
+ const range = this.geometry.passRanges[p];
1001
+ if (range.count <= 0) continue;
1002
+ gl.uniform1f(uWidth, pass.width);
890
1003
  gl.uniform3f(uColor, color[0], color[1], color[2]);
891
1004
  gl.uniform1f(uOpacity, pass.opacity);
892
- gl.drawArrays(gl.LINES, 0, this.geometry.vertexCount);
1005
+ gl.drawArrays(gl.TRIANGLES, range.start, range.count);
893
1006
  }
894
1007
  }
895
1008
  onRemove(_map, gl) {
@@ -953,17 +1066,119 @@ var LodController = class {
953
1066
  }
954
1067
  };
955
1068
 
1069
+ // src/terrain.ts
1070
+ var DEFAULT_TERRAIN_TILES = [
1071
+ "https://elevation-tiles-prod.s3.amazonaws.com/terrarium/{z}/{x}/{y}.png"
1072
+ ];
1073
+ function applyTerrain(mlMap, opts = {}) {
1074
+ const sourceId = opts.sourceId ?? "cg-dem";
1075
+ if (!mlMap.getSource(sourceId)) {
1076
+ mlMap.addSource(sourceId, {
1077
+ type: "raster-dem",
1078
+ tiles: opts.tiles ?? DEFAULT_TERRAIN_TILES,
1079
+ encoding: opts.encoding ?? "terrarium",
1080
+ tileSize: 256,
1081
+ maxzoom: opts.maxzoom ?? 15
1082
+ });
1083
+ }
1084
+ mlMap.setTerrain({ source: sourceId, exaggeration: opts.exaggeration ?? 1.5 });
1085
+ }
1086
+ function removeTerrain(mlMap, sourceId = "cg-dem") {
1087
+ mlMap.setTerrain(null);
1088
+ if (mlMap.getSource(sourceId)) {
1089
+ mlMap.removeSource(sourceId);
1090
+ }
1091
+ }
1092
+
1093
+ // src/sourceUtils.ts
1094
+ function upsertSource(mlMap, id, data) {
1095
+ if (mlMap.getSource(id)) {
1096
+ if (mlMap.setData) {
1097
+ mlMap.setData(id, data);
1098
+ } else if (mlMap.addSource) {
1099
+ mlMap.addSource(id, data);
1100
+ }
1101
+ } else if (mlMap.addSource) {
1102
+ mlMap.addSource(id, data);
1103
+ }
1104
+ }
1105
+ function removeSourceSafe(mlMap, id) {
1106
+ if (mlMap.getSource(id) && mlMap.removeSource) {
1107
+ mlMap.removeSource(id);
1108
+ }
1109
+ }
1110
+ function removeSourcesSafe(mlMap, ids) {
1111
+ for (const id of ids) {
1112
+ removeSourceSafe(mlMap, id);
1113
+ }
1114
+ }
1115
+
956
1116
  // src/index.ts
1117
+ var WebGLUnavailableError = class extends Error {
1118
+ constructor(message = "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 WebGL\uFF0C\u65E0\u6CD5\u6E32\u67D3\u5730\u56FE") {
1119
+ super(message);
1120
+ this.name = "WebGLUnavailableError";
1121
+ }
1122
+ };
1123
+ function isWebGLAvailable() {
1124
+ try {
1125
+ const canvas = document.createElement("canvas");
1126
+ let failed = false;
1127
+ const onErr = () => {
1128
+ failed = true;
1129
+ };
1130
+ canvas.addEventListener("webglcontextcreationerror", onErr, { once: true });
1131
+ const gl = canvas.getContext("webgl2") || canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
1132
+ canvas.removeEventListener("webglcontextcreationerror", onErr);
1133
+ if (failed || !gl) return false;
1134
+ const vendor = gl.getParameter(gl.VERSION);
1135
+ if (!vendor) return false;
1136
+ const vs = gl.createShader(gl.VERTEX_SHADER);
1137
+ if (!vs) return false;
1138
+ gl.shaderSource(vs, "attribute vec2 p; void main(){ gl_Position = vec4(p,0.0,1.0); }");
1139
+ gl.compileShader(vs);
1140
+ if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) return false;
1141
+ return true;
1142
+ } catch {
1143
+ return false;
1144
+ }
1145
+ }
1146
+ var GLOBAL_KEY = "__caoguoMapConfig__";
1147
+ function setGlobalConfig(cfg) {
1148
+ const prev = getGlobalConfig();
1149
+ globalThis[GLOBAL_KEY] = { ...prev, ...cfg };
1150
+ }
1151
+ function getGlobalConfig() {
1152
+ return globalThis[GLOBAL_KEY] ?? {};
1153
+ }
957
1154
  var Map2 = class {
958
1155
  constructor(options) {
959
1156
  /** 离线瓦片后端(默认浏览器 IndexedDB / Node 内存) */
960
1157
  this._store = createDefaultStore();
961
1158
  this._dataCRS = options.dataCRS ?? "WGS84";
1159
+ if (!isWebGLAvailable()) {
1160
+ throw new WebGLUnavailableError();
1161
+ }
1162
+ let style = osmRasterStyle();
1163
+ if (options.style) {
1164
+ style = options.style;
1165
+ } else if (options.tianditu) {
1166
+ if (!options.tianditu.token) throw new MissingTokenError();
1167
+ style = tiandituStyle(
1168
+ options.tianditu.type ?? "vector",
1169
+ options.tianditu
1170
+ );
1171
+ } else {
1172
+ const g = getGlobalConfig();
1173
+ if (g.tiandituToken) {
1174
+ style = tiandituStyle("vector", { token: g.tiandituToken });
1175
+ }
1176
+ }
962
1177
  this._map = new maplibregl__default.default.Map({
963
1178
  container: options.container,
964
1179
  center: options.center ?? WUHAN_CENTER,
965
1180
  zoom: options.zoom ?? WUHAN_ZOOM,
966
- style: options.style ?? osmRasterStyle(),
1181
+ style,
967
1182
  pitch: options.pitch ?? 0,
968
1183
  bearing: options.bearing ?? 0,
969
1184
  attributionControl: { compact: true }
@@ -1032,8 +1247,23 @@ var Map2 = class {
1032
1247
  else this._map.on(event, cb);
1033
1248
  }
1034
1249
  addSource(id, source) {
1250
+ if (this._map.getSource(id)) {
1251
+ try {
1252
+ this._map.removeSource(id);
1253
+ } catch {
1254
+ }
1255
+ }
1035
1256
  this._map.addSource(id, source);
1036
1257
  }
1258
+ /** 安全移除 source(不存在时静默忽略) */
1259
+ removeSource(id) {
1260
+ if (this._map.getSource(id)) {
1261
+ try {
1262
+ this._map.removeSource(id);
1263
+ } catch {
1264
+ }
1265
+ }
1266
+ }
1037
1267
  getSource(id) {
1038
1268
  return this._map.getSource(id);
1039
1269
  }
@@ -1063,6 +1293,26 @@ var Map2 = class {
1063
1293
  ctrl.addTo(container);
1064
1294
  return ctrl;
1065
1295
  }
1296
+ /**
1297
+ * 挂载图例控件(通用):渲染数据驱动的色块/线段图例,说明专题图层语义。
1298
+ * 返回控件实例,可调用 .setItems() 随图层切换更新、.remove() 卸载。
1299
+ */
1300
+ addLegendControl(options) {
1301
+ const ctrl = new LegendControl(options);
1302
+ const container = this._map.getContainer();
1303
+ ctrl.addTo(container);
1304
+ return ctrl;
1305
+ }
1306
+ /**
1307
+ * 挂载地图导出控件(通用):点击按钮将当前视图导出为 PNG 下载。
1308
+ * 返回控件实例,可调用 .remove() 卸载。
1309
+ */
1310
+ addExportControl(options = {}) {
1311
+ const ctrl = new ExportControl(this._map, options);
1312
+ const container = this._map.getContainer();
1313
+ ctrl.addTo(container);
1314
+ return ctrl;
1315
+ }
1066
1316
  /**
1067
1317
  * 挂载辉光管线 Custom Layer(T6 / F-1.3)。
1068
1318
  * 传入 GeoJSON 线集合,叠加渲染管线/路网/水系辉光效果。
@@ -1083,6 +1333,24 @@ var Map2 = class {
1083
1333
  ctrl.evaluate(true);
1084
1334
  return ctrl;
1085
1335
  }
1336
+ /**
1337
+ * 启用 3D 地形渲染(F-1.5)。
1338
+ *
1339
+ * 注入 raster-dem 高程源并调用 maplibre-gl 原生 `setTerrain`,使地图呈现地形起伏。
1340
+ * DEM 瓦片默认使用公共 Terrarium 源(elevation-tiles-prod,Terrain-RGB 编码,支持 CORS),
1341
+ * 可在 opts.tiles 覆盖为私有瓦片服务(如 MinIO / 本地 MBTiles 服务)。
1342
+ *
1343
+ * @param opts.exaggeration 地形夸张系数(默认 1.5)
1344
+ * @param opts.tiles DEM 瓦片模板,{z}/{x}/{y} 占位
1345
+ * @param opts.encoding 'terrarium'(默认,Mapzen)| 'mapbox'
1346
+ */
1347
+ enableTerrain(opts = {}) {
1348
+ applyTerrain(this._map, opts);
1349
+ }
1350
+ /** 关闭 3D 地形渲染(F-1.5),并移除 DEM 源 */
1351
+ disableTerrain(sourceId = "cg-dem") {
1352
+ removeTerrain(this._map, sourceId);
1353
+ }
1086
1354
  get instance() {
1087
1355
  return this._map;
1088
1356
  }
@@ -1096,7 +1364,9 @@ Object.defineProperty(exports, "maplibregl", {
1096
1364
  exports.CACHEABLE_HOSTS = CACHEABLE_HOSTS;
1097
1365
  exports.CACHE_NAME = CACHE_NAME;
1098
1366
  exports.CustomLineLayer = CustomLineLayer;
1367
+ exports.ExportControl = ExportControl;
1099
1368
  exports.IdbTileStore = IdbTileStore;
1369
+ exports.LegendControl = LegendControl;
1100
1370
  exports.LodController = LodController;
1101
1371
  exports.MSG_AIRGAP = MSG_AIRGAP;
1102
1372
  exports.Map = Map2;
@@ -1108,6 +1378,7 @@ exports.ScaleControl = ScaleControl;
1108
1378
  exports.ThemeSwitcher = ThemeSwitcher;
1109
1379
  exports.WUHAN_CENTER = WUHAN_CENTER;
1110
1380
  exports.WUHAN_ZOOM = WUHAN_ZOOM;
1381
+ exports.WebGLUnavailableError = WebGLUnavailableError;
1111
1382
  exports.addTiandituBaseMap = addTiandituBaseMap;
1112
1383
  exports.buildGlowGeometry = buildGlowGeometry;
1113
1384
  exports.buildTiandituSources = buildTiandituSources;
@@ -1120,9 +1391,11 @@ exports.createTransformer = createTransformer;
1120
1391
  exports.default = src_default;
1121
1392
  exports.fromWgs84 = fromWgs84;
1122
1393
  exports.gcj02ToWgs84 = gcj02ToWgs84;
1394
+ exports.getGlobalConfig = getGlobalConfig;
1123
1395
  exports.glowPasses = glowPasses;
1124
1396
  exports.installServiceWorker = installServiceWorker;
1125
1397
  exports.isInChina = isInChina;
1398
+ exports.isWebGLAvailable = isWebGLAvailable;
1126
1399
  exports.offlineGeoJSONSource = offlineGeoJSONSource;
1127
1400
  exports.offlineSourceTiles = offlineSourceTiles;
1128
1401
  exports.offlineTileUrl = offlineTileUrl;
@@ -1133,12 +1406,16 @@ exports.parseOfflineUrl = parseOfflineUrl;
1133
1406
  exports.projectSimple = projectSimple;
1134
1407
  exports.registerOfflineProtocol = registerOfflineProtocol;
1135
1408
  exports.registerOfflineServiceWorker = registerOfflineServiceWorker;
1409
+ exports.removeSourceSafe = removeSourceSafe;
1410
+ exports.removeSourcesSafe = removeSourcesSafe;
1411
+ exports.renderLegendHtml = renderLegendHtml;
1136
1412
  exports.resolveCacheKey = resolveCacheKey;
1137
1413
  exports.resolveFromStore = resolveFromStore;
1138
1414
  exports.resolveLod = resolveLod;
1139
1415
  exports.resolveResponse = resolveResponse;
1140
1416
  exports.setAirgap = setAirgap;
1141
1417
  exports.setCgcs2000GridShift = setCgcs2000GridShift;
1418
+ exports.setGlobalConfig = setGlobalConfig;
1142
1419
  exports.shouldCache = shouldCache;
1143
1420
  exports.suggestDensity = suggestDensity;
1144
1421
  exports.themeFromStyle = themeFromStyle;
@@ -1148,6 +1425,8 @@ exports.tileKey = tileKey;
1148
1425
  exports.toWgs84 = toWgs84;
1149
1426
  exports.transformBounds = transformBounds;
1150
1427
  exports.transformPoint = transformPoint;
1428
+ exports.triggerDownload = triggerDownload;
1429
+ exports.upsertSource = upsertSource;
1151
1430
  exports.wgs84ToCgcs2000 = wgs84ToCgcs2000;
1152
1431
  exports.wgs84ToGcj02 = wgs84ToGcj02;
1153
1432
  //# sourceMappingURL=index.cjs.map