@movk/maplibre 1.0.0 → 1.1.0

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.
Files changed (198) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/module.d.mts +7 -0
  4. package/dist/module.d.ts +7 -0
  5. package/dist/module.json +1 -1
  6. package/dist/module.mjs +25 -11
  7. package/dist/runtime/components/CustomLayer.d.vue.ts +1 -1
  8. package/dist/runtime/components/CustomLayer.vue +18 -4
  9. package/dist/runtime/components/CustomLayer.vue.d.ts +1 -1
  10. package/dist/runtime/components/Layer.d.vue.ts +4 -4
  11. package/dist/runtime/components/Layer.vue +43 -18
  12. package/dist/runtime/components/Layer.vue.d.ts +4 -4
  13. package/dist/runtime/components/LayerGroup.d.vue.ts +23 -10
  14. package/dist/runtime/components/LayerGroup.vue +44 -4
  15. package/dist/runtime/components/LayerGroup.vue.d.ts +23 -10
  16. package/dist/runtime/components/LottieMarker.d.vue.ts +3 -3
  17. package/dist/runtime/components/LottieMarker.vue +6 -2
  18. package/dist/runtime/components/LottieMarker.vue.d.ts +3 -3
  19. package/dist/runtime/components/Map.d.vue.ts +2 -1
  20. package/dist/runtime/components/Map.vue +2 -6
  21. package/dist/runtime/components/Map.vue.d.ts +2 -1
  22. package/dist/runtime/components/Marker.d.vue.ts +2 -1
  23. package/dist/runtime/components/Marker.vue +33 -9
  24. package/dist/runtime/components/Marker.vue.d.ts +2 -1
  25. package/dist/runtime/components/Popup.d.vue.ts +1 -1
  26. package/dist/runtime/components/Popup.vue +23 -4
  27. package/dist/runtime/components/Popup.vue.d.ts +1 -1
  28. package/dist/runtime/components/Source.d.vue.ts +2 -2
  29. package/dist/runtime/components/Source.vue +3 -15
  30. package/dist/runtime/components/Source.vue.d.ts +2 -2
  31. package/dist/runtime/components/Tooltip.d.vue.ts +2 -2
  32. package/dist/runtime/components/Tooltip.vue +47 -19
  33. package/dist/runtime/components/Tooltip.vue.d.ts +2 -2
  34. package/dist/runtime/components/buffers/BufferCircle.d.vue.ts +1 -1
  35. package/dist/runtime/components/buffers/BufferCircle.vue.d.ts +1 -1
  36. package/dist/runtime/components/buffers/BufferEllipse.d.vue.ts +1 -1
  37. package/dist/runtime/components/buffers/BufferEllipse.vue.d.ts +1 -1
  38. package/dist/runtime/components/buffers/BufferLine.d.vue.ts +1 -1
  39. package/dist/runtime/components/buffers/BufferLine.vue.d.ts +1 -1
  40. package/dist/runtime/components/buffers/BufferPolygon.d.vue.ts +1 -1
  41. package/dist/runtime/components/buffers/BufferPolygon.vue.d.ts +1 -1
  42. package/dist/runtime/components/buffers/BufferSector.d.vue.ts +1 -1
  43. package/dist/runtime/components/buffers/BufferSector.vue.d.ts +1 -1
  44. package/dist/runtime/components/controls/BasemapControl.d.vue.ts +35 -0
  45. package/dist/runtime/components/controls/BasemapControl.vue +60 -0
  46. package/dist/runtime/components/controls/BasemapControl.vue.d.ts +35 -0
  47. package/dist/runtime/components/controls/Control.d.vue.ts +51 -0
  48. package/dist/runtime/components/controls/Control.vue +95 -0
  49. package/dist/runtime/components/controls/Control.vue.d.ts +51 -0
  50. package/dist/runtime/components/controls/GlobeControl.d.vue.ts +18 -0
  51. package/dist/runtime/components/controls/GlobeControl.vue +15 -0
  52. package/dist/runtime/components/controls/GlobeControl.vue.d.ts +18 -0
  53. package/dist/runtime/components/controls/LayerControl.d.vue.ts +44 -0
  54. package/dist/runtime/components/controls/LayerControl.vue +63 -0
  55. package/dist/runtime/components/controls/LayerControl.vue.d.ts +44 -0
  56. package/dist/runtime/components/controls/Legend.d.vue.ts +47 -0
  57. package/dist/runtime/components/controls/Legend.vue +69 -0
  58. package/dist/runtime/components/controls/Legend.vue.d.ts +47 -0
  59. package/dist/runtime/components/controls/Minimap.d.vue.ts +45 -0
  60. package/dist/runtime/components/controls/Minimap.vue +31 -0
  61. package/dist/runtime/components/controls/Minimap.vue.d.ts +45 -0
  62. package/dist/runtime/components/effects/AnimatedImage.d.vue.ts +1 -1
  63. package/dist/runtime/components/effects/AnimatedImage.vue +9 -6
  64. package/dist/runtime/components/effects/AnimatedImage.vue.d.ts +1 -1
  65. package/dist/runtime/components/effects/DiffusionCircle.d.vue.ts +1 -1
  66. package/dist/runtime/components/effects/DiffusionCircle.vue.d.ts +1 -1
  67. package/dist/runtime/components/effects/FlowBuilding.d.vue.ts +1 -1
  68. package/dist/runtime/components/effects/FlowBuilding.vue.d.ts +1 -1
  69. package/dist/runtime/components/effects/GlowCircle.d.vue.ts +1 -1
  70. package/dist/runtime/components/effects/GlowCircle.vue.d.ts +1 -1
  71. package/dist/runtime/components/effects/GradientBuilding.d.vue.ts +1 -1
  72. package/dist/runtime/components/effects/GradientBuilding.vue.d.ts +1 -1
  73. package/dist/runtime/components/effects/Migration.d.vue.ts +1 -1
  74. package/dist/runtime/components/effects/Migration.vue.d.ts +1 -1
  75. package/dist/runtime/components/effects/Radar.d.vue.ts +1 -1
  76. package/dist/runtime/components/effects/Radar.vue.d.ts +1 -1
  77. package/dist/runtime/components/effects/SpriteImage.d.vue.ts +1 -1
  78. package/dist/runtime/components/effects/SpriteImage.vue +40 -24
  79. package/dist/runtime/components/effects/SpriteImage.vue.d.ts +1 -1
  80. package/dist/runtime/components/effects/TextureBuilding.d.vue.ts +1 -1
  81. package/dist/runtime/components/effects/TextureBuilding.vue +1 -1
  82. package/dist/runtime/components/effects/TextureBuilding.vue.d.ts +1 -1
  83. package/dist/runtime/components/effects/Trail.d.vue.ts +1 -1
  84. package/dist/runtime/components/effects/Trail.vue.d.ts +1 -1
  85. package/dist/runtime/components/effects/WaveCircle.d.vue.ts +1 -1
  86. package/dist/runtime/components/effects/WaveCircle.vue.d.ts +1 -1
  87. package/dist/runtime/components/effects/WindowBuilding.d.vue.ts +1 -1
  88. package/dist/runtime/components/effects/WindowBuilding.vue.d.ts +1 -1
  89. package/dist/runtime/components/environment/Projection.d.vue.ts +28 -0
  90. package/dist/runtime/components/environment/Projection.vue +27 -0
  91. package/dist/runtime/components/environment/Projection.vue.d.ts +28 -0
  92. package/dist/runtime/components/environment/Temperature.d.vue.ts +1 -1
  93. package/dist/runtime/components/environment/Temperature.vue.d.ts +1 -1
  94. package/dist/runtime/components/environment/Terrain.d.vue.ts +1 -1
  95. package/dist/runtime/components/environment/Terrain.vue +5 -0
  96. package/dist/runtime/components/environment/Terrain.vue.d.ts +1 -1
  97. package/dist/runtime/components/extensions/DrawControl.vue +23 -19
  98. package/dist/runtime/components/extensions/Swipe.d.vue.ts +48 -0
  99. package/dist/runtime/components/extensions/Swipe.vue +141 -0
  100. package/dist/runtime/components/extensions/Swipe.vue.d.ts +48 -0
  101. package/dist/runtime/components/layers/BuildingLayer.d.vue.ts +1 -1
  102. package/dist/runtime/components/layers/BuildingLayer.vue.d.ts +1 -1
  103. package/dist/runtime/components/layers/ClusterLayer.d.vue.ts +1 -1
  104. package/dist/runtime/components/layers/ClusterLayer.vue.d.ts +1 -1
  105. package/dist/runtime/components/layers/ContourLayer.d.vue.ts +59 -0
  106. package/dist/runtime/components/layers/ContourLayer.vue +84 -0
  107. package/dist/runtime/components/layers/ContourLayer.vue.d.ts +59 -0
  108. package/dist/runtime/components/layers/GraticuleLayer.d.vue.ts +31 -0
  109. package/dist/runtime/components/layers/GraticuleLayer.vue +88 -0
  110. package/dist/runtime/components/layers/GraticuleLayer.vue.d.ts +31 -0
  111. package/dist/runtime/components/layers/ImageLayer.d.vue.ts +1 -1
  112. package/dist/runtime/components/layers/ImageLayer.vue.d.ts +1 -1
  113. package/dist/runtime/components/layers/MaskLayer.d.vue.ts +44 -0
  114. package/dist/runtime/components/layers/MaskLayer.vue +32 -0
  115. package/dist/runtime/components/layers/MaskLayer.vue.d.ts +44 -0
  116. package/dist/runtime/components/layers/RasterLayer.d.vue.ts +5 -3
  117. package/dist/runtime/components/layers/RasterLayer.vue +9 -2
  118. package/dist/runtime/components/layers/RasterLayer.vue.d.ts +5 -3
  119. package/dist/runtime/components/layers/VideoLayer.d.vue.ts +1 -1
  120. package/dist/runtime/components/layers/VideoLayer.vue.d.ts +1 -1
  121. package/dist/runtime/composables/useFeatureState.js +27 -5
  122. package/dist/runtime/composables/useFrameIcon.d.ts +4 -3
  123. package/dist/runtime/composables/useFrameIcon.js +18 -8
  124. package/dist/runtime/composables/useLayerTree.d.ts +11 -0
  125. package/dist/runtime/composables/useLayerTree.js +12 -0
  126. package/dist/runtime/composables/useMapAnimation.d.ts +4 -2
  127. package/dist/runtime/composables/useMapAnimation.js +5 -4
  128. package/dist/runtime/composables/useMapExport.js +2 -5
  129. package/dist/runtime/composables/useMapSync.d.ts +16 -0
  130. package/dist/runtime/composables/useMapSync.js +42 -0
  131. package/dist/runtime/composables/useMaplibreCamera.d.ts +1 -1
  132. package/dist/runtime/composables/useMaplibreCamera.js +1 -1
  133. package/dist/runtime/composables/useMaplibreImage.d.ts +3 -3
  134. package/dist/runtime/composables/useMaplibreImage.js +26 -5
  135. package/dist/runtime/domains/map/config.js +2 -3
  136. package/dist/runtime/domains/map/context.js +15 -0
  137. package/dist/runtime/domains/map/control.d.ts +1 -1
  138. package/dist/runtime/domains/map/control.js +1 -1
  139. package/dist/runtime/domains/map/draw-modes.d.ts +9 -0
  140. package/dist/runtime/domains/map/draw-modes.js +53 -0
  141. package/dist/runtime/domains/map/draw-registry.d.ts +2 -3
  142. package/dist/runtime/domains/map/draw-registry.js +6 -9
  143. package/dist/runtime/domains/map/draw-theme.d.ts +12 -0
  144. package/dist/runtime/domains/map/draw-toolbar.js +11 -10
  145. package/dist/runtime/domains/map/layer-group.d.ts +15 -4
  146. package/dist/runtime/domains/map/layer-tree.d.ts +3 -0
  147. package/dist/runtime/domains/map/layer-tree.js +10 -0
  148. package/dist/runtime/domains/map/minimap.d.ts +18 -0
  149. package/dist/runtime/domains/map/minimap.js +86 -0
  150. package/dist/runtime/domains/map/registry.js +4 -3
  151. package/dist/runtime/domains/map/style-layers.d.ts +7 -0
  152. package/dist/runtime/domains/map/style-layers.js +54 -0
  153. package/dist/runtime/domains/map/style.d.ts +3 -0
  154. package/dist/runtime/domains/map/style.js +5 -0
  155. package/dist/runtime/domains/map/swipe.d.ts +9 -0
  156. package/dist/runtime/domains/map/swipe.js +62 -0
  157. package/dist/runtime/index.css +1 -1
  158. package/dist/runtime/types/draw.d.ts +34 -1
  159. package/dist/runtime/types/index.d.ts +1 -0
  160. package/dist/runtime/types/layer.d.ts +40 -0
  161. package/dist/runtime/types/layer.js +0 -0
  162. package/dist/runtime/types/map.d.ts +6 -0
  163. package/dist/runtime/utils/cog.d.ts +2 -0
  164. package/dist/runtime/utils/cog.js +9 -0
  165. package/dist/runtime/utils/effects.js +2 -1
  166. package/dist/runtime/utils/events.d.ts +6 -0
  167. package/dist/runtime/utils/events.js +25 -0
  168. package/dist/runtime/utils/frame-icon.d.ts +2 -5
  169. package/dist/runtime/utils/frame-icon.js +10 -4
  170. package/dist/runtime/utils/graticule.d.ts +19 -0
  171. package/dist/runtime/utils/graticule.js +62 -0
  172. package/dist/runtime/utils/layer-opacity.d.ts +7 -0
  173. package/dist/runtime/utils/layer-opacity.js +59 -0
  174. package/dist/runtime/utils/layer.js +2 -1
  175. package/dist/runtime/utils/legend.d.ts +9 -0
  176. package/dist/runtime/utils/legend.js +52 -0
  177. package/dist/runtime/utils/marker.d.ts +11 -0
  178. package/dist/runtime/utils/marker.js +28 -0
  179. package/dist/runtime/utils/mask.d.ts +8 -0
  180. package/dist/runtime/utils/mask.js +33 -0
  181. package/dist/runtime/utils/minimap.d.ts +6 -0
  182. package/dist/runtime/utils/minimap.js +15 -0
  183. package/dist/runtime/utils/pmtiles.d.ts +3 -0
  184. package/dist/runtime/utils/pmtiles.js +10 -0
  185. package/dist/runtime/utils/source.d.ts +7 -0
  186. package/dist/runtime/utils/source.js +35 -0
  187. package/dist/runtime/utils/swipe.d.ts +32 -0
  188. package/dist/runtime/utils/swipe.js +18 -0
  189. package/dist/runtime/utils/tianditu.d.ts +10 -1
  190. package/dist/runtime/utils/tianditu.js +11 -0
  191. package/dist/unplugin.d.mts +1 -1
  192. package/dist/unplugin.d.ts +1 -1
  193. package/dist/unplugin.mjs +1 -7
  194. package/package.json +20 -12
  195. package/dist/runtime/draw-modes/index.d.ts +0 -16
  196. package/dist/runtime/draw-modes/index.js +0 -32
  197. package/dist/runtime/utils/draw-theme.d.ts +0 -47
  198. /package/dist/runtime/{utils → domains/map}/draw-theme.js +0 -0
@@ -14,18 +14,24 @@ export function pickFrameIndex(elapsedMs, durations, fps, total) {
14
14
  }
15
15
  export function createFrameStyleImage(options) {
16
16
  const { size, frames, getMap } = options;
17
- const fps = options.fps ?? 12;
17
+ const byteLength = size * size * 4;
18
18
  let start = 0;
19
19
  let lastIndex = -1;
20
20
  let committed = false;
21
+ let current;
21
22
  const image = {
22
23
  width: size,
23
24
  height: size,
24
- data: new Uint8Array(size * size * 4),
25
+ data: new Uint8Array(byteLength),
25
26
  render() {
26
27
  const fs = frames();
28
+ if (fs !== current) {
29
+ current = fs;
30
+ start = 0;
31
+ lastIndex = -1;
32
+ }
27
33
  const total = fs.length;
28
- if (!total) {
34
+ if (!total || fs[0].data.length !== byteLength) {
29
35
  if (committed) return false;
30
36
  committed = true;
31
37
  return true;
@@ -34,7 +40,7 @@ export function createFrameStyleImage(options) {
34
40
  if (map) map.triggerRepaint();
35
41
  committed = true;
36
42
  if (!start) start = performance.now();
37
- const index = pickFrameIndex(performance.now() - start, options.durations?.(), fps, total);
43
+ const index = pickFrameIndex(performance.now() - start, options.durations?.(), options.fps?.() ?? 12, total);
38
44
  if (index === lastIndex) return false;
39
45
  lastIndex = index;
40
46
  image.data = fs[index].data;
@@ -0,0 +1,19 @@
1
+ import type { FeatureCollection, LineString, Point, Position } from 'geojson';
2
+ /** 经纬度范围 [west, south, east, north] */
3
+ export type Bounds = [number, number, number, number];
4
+ type Axis = 'lon' | 'lat';
5
+ /** 按缩放级别选择步长,使 512px 视口宽度内约有 4 条以上经线 */
6
+ export declare function graticuleStep(zoom: number): number;
7
+ /** 度数标注,如 `30°N`、`120°E`、`0°`;经度归一化到 (-180, 180] */
8
+ export declare function formatDegree(value: number, axis: Axis): string;
9
+ /** 生成外扩范围内的经线与纬线(均已加密),并返回该范围供判断是否需要重算 */
10
+ export declare function graticuleLines(bounds: Bounds, step: number): {
11
+ extent: Bounds;
12
+ data: FeatureCollection<LineString>;
13
+ };
14
+ /**
15
+ * 生成视口内的度数标注:经线标注位于 anchor 纬度,纬线标注位于 anchor 经度;
16
+ * anchor 缺省为视口西南角(沿南、西边缘排布),世界视图可传地图中心
17
+ */
18
+ export declare function graticuleLabels([west, south, east, north]: Bounds, step: number, anchor?: Position): FeatureCollection<Point>;
19
+ export {};
@@ -0,0 +1,62 @@
1
+ const STEPS = [30, 10, 5, 2, 1, 0.5, 0.25, 0.1];
2
+ const MIN_LINES = 4;
3
+ const MAX_LAT = 85;
4
+ const DENSIFY = 1;
5
+ const round = (value) => Math.round(value * 1e6) / 1e6;
6
+ function snap(value, fn) {
7
+ const nearest = Math.round(value);
8
+ return Math.abs(value - nearest) < 1e-9 ? nearest : fn(value);
9
+ }
10
+ export function graticuleStep(zoom) {
11
+ const span = 360 / 2 ** zoom;
12
+ return STEPS.find((step) => span / step >= MIN_LINES) ?? STEPS.at(-1);
13
+ }
14
+ export function formatDegree(value, axis) {
15
+ const normalized = axis === "lon" ? value - 360 * Math.ceil((value - 180) / 360) : value;
16
+ const abs = round(Math.abs(normalized));
17
+ if (abs === 0 || axis === "lon" && abs === 180) return `${abs}\xB0`;
18
+ const hemisphere = axis === "lon" ? normalized > 0 ? "E" : "W" : normalized > 0 ? "N" : "S";
19
+ return `${abs}\xB0${hemisphere}`;
20
+ }
21
+ function multiples(from, to, step) {
22
+ const first = snap(from / step, Math.ceil);
23
+ const last = snap(to / step, Math.floor);
24
+ return Array.from({ length: Math.max(0, last - first + 1) }, (_, i) => round((first + i) * step));
25
+ }
26
+ function densify(from, to, interval) {
27
+ const count = Math.max(1, Math.ceil(round((to - from) / interval)));
28
+ return Array.from({ length: count + 1 }, (_, i) => round(Math.min(to, from + i * interval)));
29
+ }
30
+ function expand([west, south, east, north], step) {
31
+ const fullLon = west < -180 || east > 180;
32
+ return [
33
+ fullLon ? -180 : Math.max(-180, round((snap(west / step, Math.floor) - 1) * step)),
34
+ Math.max(-MAX_LAT, round((snap(south / step, Math.floor) - 1) * step)),
35
+ fullLon ? 180 : Math.min(180, round((snap(east / step, Math.ceil) + 1) * step)),
36
+ Math.min(MAX_LAT, round((snap(north / step, Math.ceil) + 1) * step))
37
+ ];
38
+ }
39
+ function line(coordinates, axis, value) {
40
+ return { type: "Feature", properties: { axis, value }, geometry: { type: "LineString", coordinates } };
41
+ }
42
+ export function graticuleLines(bounds, step) {
43
+ const extent = expand(bounds, step);
44
+ const [west, south, east, north] = extent;
45
+ const interval = Math.min(step, DENSIFY);
46
+ const lats = densify(south, north, interval);
47
+ const lons = densify(west, east, interval);
48
+ const meridianValues = multiples(west, east, step).filter((lon) => !(east - west >= 360 && lon === 180));
49
+ const meridians = meridianValues.map((lon) => line(lats.map((lat) => [lon, lat]), "lon", lon));
50
+ const parallels = multiples(south, north, step).map((lat) => line(lons.map((lon) => [lon, lat]), "lat", lat));
51
+ return { extent, data: { type: "FeatureCollection", features: [...meridians, ...parallels] } };
52
+ }
53
+ function label(coordinates, axis, value) {
54
+ return { type: "Feature", properties: { axis, label: formatDegree(value, axis) }, geometry: { type: "Point", coordinates } };
55
+ }
56
+ export function graticuleLabels([west, south, east, north], step, anchor = [west, south]) {
57
+ const [anchorLon = west, anchorLat = south] = anchor;
58
+ const lonValues = multiples(west, east, step).filter((lon2) => !(east - west >= 360 && lon2 === 180));
59
+ const lon = lonValues.map((value) => label([value, anchorLat], "lon", value));
60
+ const lat = multiples(Math.max(south, -MAX_LAT), Math.min(north, MAX_LAT), step).map((value) => label([anchorLon, value], "lat", value));
61
+ return { type: "FeatureCollection", features: [...lon, ...lat] };
62
+ }
@@ -0,0 +1,7 @@
1
+ import type { LayerSpecification } from '@maplibre/maplibre-gl-style-spec';
2
+ type LayerType = LayerSpecification['type'];
3
+ /** 图层类型对应的透明度属性 */
4
+ export declare function opacityPropsFor(type: LayerType): readonly string[];
5
+ /** 透明度按系数缩放;系数为 1 时原样返回,未设置视为默认值 1 */
6
+ export declare function scaleOpacity(value: unknown, factor: number): unknown;
7
+ export {};
@@ -0,0 +1,59 @@
1
+ import { logger } from "./logger.js";
2
+ const OPACITY_PROPS = {
3
+ "fill": ["fill-opacity"],
4
+ "line": ["line-opacity"],
5
+ "circle": ["circle-opacity", "circle-stroke-opacity"],
6
+ "symbol": ["icon-opacity", "text-opacity"],
7
+ "raster": ["raster-opacity"],
8
+ "fill-extrusion": ["fill-extrusion-opacity"],
9
+ "heatmap": ["heatmap-opacity"],
10
+ "background": ["background-opacity"]
11
+ };
12
+ export function opacityPropsFor(type) {
13
+ return OPACITY_PROPS[type] ?? [];
14
+ }
15
+ function containsZoom(value) {
16
+ if (!Array.isArray(value)) return false;
17
+ if (value[0] === "zoom" && value.length === 1) return true;
18
+ return value.some(containsZoom);
19
+ }
20
+ function isZoomInput(value) {
21
+ return Array.isArray(value) && value[0] === "zoom" && value.length === 1;
22
+ }
23
+ function scaleOutput(value, factor) {
24
+ if (typeof value === "number") return value * factor;
25
+ if (containsZoom(value)) return void 0;
26
+ return ["*", value, factor];
27
+ }
28
+ function scaleZoomCurve(expr, factor) {
29
+ const [op] = expr;
30
+ const outputStart = op === "interpolate" ? 4 : 2;
31
+ const scaled = [...expr];
32
+ for (let i = outputStart; i < expr.length; i += 2) {
33
+ const next = scaleOutput(expr[i], factor);
34
+ if (next === void 0) return void 0;
35
+ scaled[i] = next;
36
+ }
37
+ return scaled;
38
+ }
39
+ let warned = false;
40
+ export function scaleOpacity(value, factor) {
41
+ if (factor === 1) return value;
42
+ if (value === void 0) return factor;
43
+ if (typeof value === "number") return value * factor;
44
+ if (Array.isArray(value)) {
45
+ const [op] = value;
46
+ const input = op === "interpolate" ? value[2] : op === "step" ? value[1] : void 0;
47
+ if (isZoomInput(input)) {
48
+ const scaled = scaleZoomCurve(value, factor);
49
+ if (scaled) return scaled;
50
+ } else if (!containsZoom(value)) {
51
+ return ["*", value, factor];
52
+ }
53
+ }
54
+ if (!warned) {
55
+ warned = true;
56
+ logger.warn("Opacity expression cannot be scaled safely (zoom used outside a top-level interpolate/step); layer group opacity is ignored for it.", value);
57
+ }
58
+ return value;
59
+ }
@@ -1,6 +1,7 @@
1
1
  import { equalsBy } from "@movk/core";
2
2
  function applyChangedProps(next, prev, apply) {
3
- for (const key of Object.keys(next)) {
3
+ const keys = /* @__PURE__ */ new Set([...Object.keys(prev), ...Object.keys(next)]);
4
+ for (const key of keys) {
4
5
  if (!equalsBy(next[key], prev[key])) apply(key, next[key]);
5
6
  }
6
7
  }
@@ -0,0 +1,9 @@
1
+ import type { LayerSpecification } from '@maplibre/maplibre-gl-style-spec';
2
+ import type { LegendItem } from '../types/layer';
3
+ /** 参与图例推导的图层描述 */
4
+ export interface LegendLayer {
5
+ type: LayerSpecification['type'];
6
+ paint?: Record<string, unknown>;
7
+ }
8
+ /** 由图层颜色推导图例项:支持字面量、match、step、interpolate,同组内去重 */
9
+ export declare function deriveLegend(layers: LegendLayer[], title: string): LegendItem[];
@@ -0,0 +1,52 @@
1
+ const COLOR_PROPS = {
2
+ "fill": ["fill-color", "fill"],
3
+ "line": ["line-color", "line"],
4
+ "circle": ["circle-color", "circle"],
5
+ "fill-extrusion": ["fill-extrusion-color", "fill"]
6
+ };
7
+ const isZoomInput = (value) => Array.isArray(value) && value[0] === "zoom";
8
+ function fromMatch(expr, type) {
9
+ const items = [];
10
+ for (let i = 2; i + 1 < expr.length; i += 2) {
11
+ const [label, color] = [expr[i], expr[i + 1]];
12
+ if (typeof color !== "string") continue;
13
+ items.push({ label: Array.isArray(label) ? label.join(", ") : String(label), type, color });
14
+ }
15
+ return items;
16
+ }
17
+ function fromStep(expr, type) {
18
+ const colors = expr.filter((_, i) => i >= 2 && i % 2 === 0);
19
+ const stops = expr.filter((_, i) => i >= 3 && i % 2 === 1);
20
+ if (colors.some((color) => typeof color !== "string")) return [];
21
+ return colors.map((color, i) => {
22
+ const label = i === 0 ? `< ${stops[0]}` : i === stops.length ? `\u2265 ${stops[i - 1]}` : `${stops[i - 1]} \u2013 ${stops[i]}`;
23
+ return { label, type, color };
24
+ });
25
+ }
26
+ function fromInterpolate(expr) {
27
+ const stops = expr.filter((_, i) => i >= 3 && i % 2 === 1);
28
+ const colors = expr.filter((_, i) => i >= 4 && i % 2 === 0);
29
+ if (!colors.length || colors.some((color) => typeof color !== "string")) return [];
30
+ return [{ label: `${stops[0]} \u2013 ${stops.at(-1)}`, type: "gradient", colors }];
31
+ }
32
+ function itemsFor(value, type, title) {
33
+ if (typeof value === "string") return [{ label: title, type, color: value }];
34
+ if (!Array.isArray(value)) return [];
35
+ const [op] = value;
36
+ if (op === "match") return fromMatch(value, type);
37
+ if (op === "step" && !isZoomInput(value[1])) return fromStep(value, type);
38
+ if (op === "interpolate" && !isZoomInput(value[2])) return fromInterpolate(value);
39
+ return [];
40
+ }
41
+ export function deriveLegend(layers, title) {
42
+ const seen = /* @__PURE__ */ new Set();
43
+ return layers.flatMap((layer) => {
44
+ const entry = COLOR_PROPS[layer.type];
45
+ return entry ? itemsFor(layer.paint?.[entry[0]], entry[1], title) : [];
46
+ }).filter((item) => {
47
+ const key = JSON.stringify(item);
48
+ if (seen.has(key)) return false;
49
+ seen.add(key);
50
+ return true;
51
+ });
52
+ }
@@ -0,0 +1,11 @@
1
+ import type { Marker, MarkerOptions } from 'maplibre-gl';
2
+ export type MarkerInputOptions = Omit<MarkerOptions, 'element'>;
3
+ /**
4
+ * 判断选项变化是否需要重建 marker:anchor/color/scale 无 setter,
5
+ * offset 移除后的缺省值取决于是否默认水滴 pin,交由构造函数决定。
6
+ */
7
+ export declare function markerNeedsRebuild(next: MarkerInputOptions, prev: MarkerInputOptions): boolean;
8
+ /** 移除 className 选项加到 marker 元素上的类(重建复用插槽元素前清理残留) */
9
+ export declare function removeMarkerClassName(marker: Marker, className?: string): void;
10
+ /** 以 setter 增量下发有差异的选项,调用方需先经 markerNeedsRebuild 排除无 setter 的字段 */
11
+ export declare function applyMarkerOptions(marker: Marker, next: MarkerInputOptions, prev: MarkerInputOptions): void;
@@ -0,0 +1,28 @@
1
+ import { isDeepEqual } from "@movk/core";
2
+ function classNames(value) {
3
+ return value?.split(" ").filter(Boolean) ?? [];
4
+ }
5
+ export function markerNeedsRebuild(next, prev) {
6
+ return next.anchor !== prev.anchor || next.color !== prev.color || next.scale !== prev.scale || next.offset === void 0 && prev.offset !== void 0;
7
+ }
8
+ export function removeMarkerClassName(marker, className) {
9
+ for (const name of classNames(className)) marker.removeClassName(name);
10
+ }
11
+ export function applyMarkerOptions(marker, next, prev) {
12
+ if (next.draggable !== prev.draggable) marker.setDraggable(next.draggable);
13
+ if (next.rotation !== prev.rotation) marker.setRotation(next.rotation);
14
+ if (next.rotationAlignment !== prev.rotationAlignment || next.pitchAlignment !== prev.pitchAlignment) {
15
+ marker.setRotationAlignment(next.rotationAlignment);
16
+ marker.setPitchAlignment(next.pitchAlignment);
17
+ }
18
+ if (next.offset !== void 0 && !isDeepEqual(next.offset, prev.offset)) marker.setOffset(next.offset);
19
+ if (next.opacity !== prev.opacity || next.opacityWhenCovered !== prev.opacityWhenCovered) {
20
+ marker.setOpacity();
21
+ marker.setOpacity(next.opacity, next.opacityWhenCovered);
22
+ }
23
+ if (next.subpixelPositioning !== prev.subpixelPositioning) marker.setSubpixelPositioning(next.subpixelPositioning ?? false);
24
+ if (next.className !== prev.className) {
25
+ removeMarkerClassName(marker, prev.className);
26
+ for (const name of classNames(next.className)) marker.addClassName(name);
27
+ }
28
+ }
@@ -0,0 +1,8 @@
1
+ import type { Feature, FeatureCollection, MultiPolygon, Polygon } from 'geojson';
2
+ /** 遮罩输入:面状几何、要素或要素集合;非面状要素被忽略 */
3
+ export type MaskInput = Polygon | MultiPolygon | Feature | FeatureCollection;
4
+ /**
5
+ * 生成反向遮罩:以全球范围为外环,把目标区域各多边形的外环作为孔洞。
6
+ * 目标自身的孔洞(飞地)不参与;无面状要素时返回空集合。
7
+ */
8
+ export declare function maskPolygon(data: MaskInput): Feature<Polygon> | FeatureCollection;
@@ -0,0 +1,33 @@
1
+ const MAX_LAT = 85.0511;
2
+ const WORLD_RING = [[-180, -MAX_LAT], [180, -MAX_LAT], [180, MAX_LAT], [-180, MAX_LAT], [-180, -MAX_LAT]];
3
+ function signedArea(ring) {
4
+ let sum = 0;
5
+ for (let i = 0; i < ring.length - 1; i++) {
6
+ const [x1 = 0, y1 = 0] = ring[i];
7
+ const [x2 = 0, y2 = 0] = ring[i + 1];
8
+ sum += x1 * y2 - x2 * y1;
9
+ }
10
+ return sum / 2;
11
+ }
12
+ function toClockwise(ring) {
13
+ return signedArea(ring) > 0 ? [...ring].reverse() : ring;
14
+ }
15
+ function outerRings(geometry) {
16
+ if (geometry?.type === "Polygon") return geometry.coordinates.slice(0, 1);
17
+ if (geometry?.type === "MultiPolygon") return geometry.coordinates.map((polygon) => polygon[0]).filter(Boolean);
18
+ return [];
19
+ }
20
+ function collectRings(data) {
21
+ if (data.type === "FeatureCollection") return data.features.flatMap((feature) => outerRings(feature.geometry));
22
+ if (data.type === "Feature") return outerRings(data.geometry);
23
+ return outerRings(data);
24
+ }
25
+ export function maskPolygon(data) {
26
+ const holes = collectRings(data).filter((ring) => ring.length >= 4).map(toClockwise);
27
+ if (!holes.length) return { type: "FeatureCollection", features: [] };
28
+ return {
29
+ type: "Feature",
30
+ properties: {},
31
+ geometry: { type: "Polygon", coordinates: [WORLD_RING, ...holes] }
32
+ };
33
+ }
@@ -0,0 +1,6 @@
1
+ import type { Feature, Polygon } from 'geojson';
2
+ import type { Map as MaplibreMap } from 'maplibre-gl';
3
+ /** 鹰眼缩放级别:主图缩放叠加偏移,不低于 0 */
4
+ export declare function minimapZoom(parentZoom: number, offset: number): number;
5
+ /** 主图视口多边形:容器四角反投影,旋转、倾斜时同样贴合可视范围 */
6
+ export declare function viewportPolygon(map: Pick<MaplibreMap, 'getContainer' | 'unproject'>): Feature<Polygon>;
@@ -0,0 +1,15 @@
1
+ export function minimapZoom(parentZoom, offset) {
2
+ return Math.max(parentZoom + offset, 0);
3
+ }
4
+ export function viewportPolygon(map) {
5
+ const { clientWidth: w, clientHeight: h } = map.getContainer();
6
+ const ring = [[0, 0], [w, 0], [w, h], [0, h]].map(([x, y]) => {
7
+ const { lng, lat } = map.unproject([x, y]);
8
+ return [lng, lat];
9
+ });
10
+ return {
11
+ type: "Feature",
12
+ properties: {},
13
+ geometry: { type: "Polygon", coordinates: [[...ring, ring[0]]] }
14
+ };
15
+ }
@@ -0,0 +1,3 @@
1
+ import { Protocol } from 'pmtiles';
2
+ /** 注册 `pmtiles://` 协议(全局单例、幂等);返回 Protocol 实例,可经 `protocol.add(new PMTiles(...))` 登记自定义来源 */
3
+ export declare function registerPmtilesProtocol(options?: ConstructorParameters<typeof Protocol>[0]): Protocol;
@@ -0,0 +1,10 @@
1
+ import { addProtocol } from "maplibre-gl";
2
+ import { Protocol } from "pmtiles";
3
+ import { defineGlobalSingleton } from "@movk/core";
4
+ export function registerPmtilesProtocol(options) {
5
+ return defineGlobalSingleton("movk-maplibre:protocol:pmtiles", () => {
6
+ const protocol = new Protocol(options);
7
+ addProtocol("pmtiles", protocol.tile);
8
+ return protocol;
9
+ });
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { Source } from 'maplibre-gl';
2
+ import type { SourceSpecification } from '@maplibre/maplibre-gl-style-spec';
3
+ /**
4
+ * 按类型把 source 定义的变化增量应用到已有源,避免整源重建。
5
+ * prev 与 next 为同一引用(原地修改)时仅按 next 下发数据;类型变化无法原地更新,告警后跳过。
6
+ */
7
+ export declare function updateSource(source: Source, next: SourceSpecification, prev?: SourceSpecification): void;
@@ -0,0 +1,35 @@
1
+ import { omitUndefined } from "@movk/core";
2
+ import { logger } from "./logger.js";
3
+ const CLUSTER_KEYS = ["cluster", "clusterRadius", "clusterMaxZoom"];
4
+ function clusterChanged(next, prev) {
5
+ if (!prev || prev === next) return false;
6
+ const previous = prev;
7
+ return CLUSTER_KEYS.some((key) => next[key] !== previous[key]);
8
+ }
9
+ export function updateSource(source, next, prev) {
10
+ if (prev && prev.type !== next.type) {
11
+ logger.warn(`Source type changed from "${prev.type}" to "${next.type}"; recreate it with a new :key instead.`);
12
+ return;
13
+ }
14
+ if (next.type === "geojson") {
15
+ const geojson = source;
16
+ if (next.data) geojson.setData(next.data);
17
+ if (clusterChanged(next, prev)) {
18
+ geojson.setClusterOptions(omitUndefined({
19
+ cluster: next.cluster,
20
+ clusterRadius: next.clusterRadius,
21
+ clusterMaxZoom: next.clusterMaxZoom
22
+ })).catch((error) => logger.error("Failed to update cluster options:", error));
23
+ }
24
+ } else if (next.type === "vector") {
25
+ if (next.url) source.setUrl(next.url);
26
+ if (next.tiles) source.setTiles(next.tiles);
27
+ } else if (next.type === "raster" || next.type === "raster-dem") {
28
+ if (next.url) source.setUrl(next.url);
29
+ if (next.tiles) source.setTiles(next.tiles);
30
+ } else if (next.type === "image" && next.url) {
31
+ source.updateImage(next);
32
+ } else if (next.type === "video" && next.coordinates) {
33
+ source.setCoordinates(next.coordinates);
34
+ }
35
+ }
@@ -0,0 +1,32 @@
1
+ /** 卷帘分隔方向:vertical 为竖直分隔条左右对比,horizontal 为水平分隔条上下对比 */
2
+ export type SwipeOrientation = 'vertical' | 'horizontal';
3
+ interface Point {
4
+ x: number;
5
+ y: number;
6
+ }
7
+ interface Rect {
8
+ left: number;
9
+ top: number;
10
+ width: number;
11
+ height: number;
12
+ }
13
+ /** 对照图裁切:只露出分隔条之后(右侧或下侧)的部分 */
14
+ export declare function swipeClipPath(position: number, orientation: SwipeOrientation): string;
15
+ /** 指针坐标换算为分隔条在容器内的比例(0..1) */
16
+ export declare function positionFromPointer(rect: Rect, point: Point, orientation: SwipeOrientation): number;
17
+ /** 容器内的点是否落在对照图露出区域(分隔条上视为露出) */
18
+ export declare function isPointRevealed(point: Point, size: {
19
+ width: number;
20
+ height: number;
21
+ }, position: number, orientation: SwipeOrientation): boolean;
22
+ interface AttributionSource {
23
+ getStyle: () => {
24
+ sources?: object;
25
+ } | undefined;
26
+ getSource: (id: string) => {
27
+ attribution?: string;
28
+ } | undefined;
29
+ }
30
+ /** 汇总地图各来源的署名,按来源顺序去重 */
31
+ export declare function collectAttributions(map: AttributionSource): string[];
32
+ export {};
@@ -0,0 +1,18 @@
1
+ import { clamp } from "@movk/core";
2
+ export function swipeClipPath(position, orientation) {
3
+ const percent = `${clamp(position, 0, 1) * 100}%`;
4
+ return orientation === "vertical" ? `inset(0 0 0 ${percent})` : `inset(${percent} 0 0 0)`;
5
+ }
6
+ export function positionFromPointer(rect, point, orientation) {
7
+ const [offset, size] = orientation === "vertical" ? [point.x - rect.left, rect.width] : [point.y - rect.top, rect.height];
8
+ return size > 0 ? clamp(offset / size, 0, 1) : 0;
9
+ }
10
+ export function isPointRevealed(point, size, position, orientation) {
11
+ const ratio = clamp(position, 0, 1);
12
+ return orientation === "vertical" ? point.x >= ratio * size.width : point.y >= ratio * size.height;
13
+ }
14
+ export function collectAttributions(map) {
15
+ const ids = Object.keys(map.getStyle()?.sources ?? {});
16
+ const texts = ids.map((id) => map.getSource(id)?.attribution?.trim()).filter((text) => Boolean(text));
17
+ return [...new Set(texts)];
18
+ }
@@ -1,4 +1,4 @@
1
- import type { RasterSourceSpecification } from '@maplibre/maplibre-gl-style-spec';
1
+ import type { RasterSourceSpecification, StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
2
2
  /** 天地图图层类型:vec 矢量底图、img 影像底图、ter 地形、cva 矢量注记、cia 影像注记、cta 地形注记 */
3
3
  export type TiandituLayerType = 'vec' | 'img' | 'ter' | 'cva' | 'cia' | 'cta';
4
4
  /** 取底图对应的注记图层类型;注记类型自身或无对应时返回 undefined。 */
@@ -19,3 +19,12 @@ export interface TiandituSourceOptions {
19
19
  }
20
20
  /** 生成天地图球面墨卡托(EPSG:3857)栅格数据源规格,可直接用于 MaplibreSource。 */
21
21
  export declare function tiandituRasterSource(layer: TiandituLayerType, options?: TiandituSourceOptions): RasterSourceSpecification;
22
+ export interface TiandituStyleOptions extends TiandituSourceOptions {
23
+ /** 叠加对应注记图层(vec→cva / img→cia / ter→cta) */
24
+ annotation?: boolean;
25
+ }
26
+ /**
27
+ * 生成以天地图为底图的完整样式,可直接作为 MaplibreMap 的 options.style 或底图切换项,
28
+ * 与矢量样式经同一 setStyle 机制切换;glyphs 取自运行时配置,便于叠加文字图层。
29
+ */
30
+ export declare function tiandituStyle(layer: TiandituLayerType, options?: TiandituStyleOptions): StyleSpecification;
@@ -1,4 +1,5 @@
1
1
  import { getMaplibreConfig } from "../domains/map/config.js";
2
+ import { blankStyle } from "../domains/map/style.js";
2
3
  import { logger } from "./logger.js";
3
4
  import { wmtsRasterSource } from "./wmts.js";
4
5
  const ANNOTATION_MAP = {
@@ -24,3 +25,13 @@ export function tiandituRasterSource(layer, options = {}) {
24
25
  params: { tk: tk ?? "" }
25
26
  });
26
27
  }
28
+ export function tiandituStyle(layer, options = {}) {
29
+ const { annotation, ...sourceOptions } = options;
30
+ const types = [layer, annotation ? tiandituAnnotationFor(layer) : void 0].filter((type) => type !== void 0);
31
+ const style = blankStyle();
32
+ return {
33
+ ...style,
34
+ sources: Object.fromEntries(types.map((type) => [`tianditu-${type}`, tiandituRasterSource(type, sourceOptions)])),
35
+ layers: types.map((type) => ({ id: `tianditu-${type}`, type: "raster", source: `tianditu-${type}` }))
36
+ };
37
+ }
@@ -19,7 +19,7 @@ declare function maplibreComponentResolver(options?: MaplibreResolverOptions): {
19
19
  from: string;
20
20
  } | undefined;
21
21
  };
22
- /** 本库 composables + 工具的自动导入项,注入既有 unplugin-auto-import 实例复用 */
22
+ /** 本库 composables 的自动导入项,注入既有 unplugin-auto-import 实例复用 */
23
23
  declare function maplibreAutoImports(): {
24
24
  from: string;
25
25
  imports: string[];
@@ -19,7 +19,7 @@ declare function maplibreComponentResolver(options?: MaplibreResolverOptions): {
19
19
  from: string;
20
20
  } | undefined;
21
21
  };
22
- /** 本库 composables + 工具的自动导入项,注入既有 unplugin-auto-import 实例复用 */
22
+ /** 本库 composables 的自动导入项,注入既有 unplugin-auto-import 实例复用 */
23
23
  declare function maplibreAutoImports(): {
24
24
  from: string;
25
25
  imports: string[];
package/dist/unplugin.mjs CHANGED
@@ -6,10 +6,6 @@ import Components from 'unplugin-vue-components';
6
6
  import AutoImport from 'unplugin-auto-import';
7
7
 
8
8
  const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
9
- const UTIL_MANIFEST = {
10
- movkDrawModes: "draw-modes",
11
- drawThemeStyles: "utils/draw-theme"
12
- };
13
9
  function componentMap() {
14
10
  const dir = join(runtimeDir, "components");
15
11
  const map = /* @__PURE__ */ new Map();
@@ -33,9 +29,7 @@ function maplibreComponentResolver(options = {}) {
33
29
  }
34
30
  function maplibreAutoImports() {
35
31
  const cdir = join(runtimeDir, "composables");
36
- const composables = readdirSync(cdir).filter((f) => f.endsWith(".ts")).map((f) => f.slice(0, -".ts".length)).map((name) => ({ from: join(cdir, name), imports: [name] }));
37
- const utils = Object.entries(UTIL_MANIFEST).map(([name, file]) => ({ from: join(runtimeDir, file), imports: [name] }));
38
- return [...composables, ...utils];
32
+ return readdirSync(cdir).filter((f) => f.endsWith(".ts")).map((f) => f.slice(0, -".ts".length)).map((name) => ({ from: join(cdir, name), imports: [name] }));
39
33
  }
40
34
  const MaplibreUnplugin = createUnplugin((options = {}, meta) => {
41
35
  const dts = options.dts ?? true;