my-openlayer 2.5.4 → 3.0.1
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.
- package/CHANGELOG.md +68 -0
- package/MyOl.d.ts +3 -22
- package/MyOl.js +52 -112
- package/README.md +116 -29
- package/core/line/Line.d.ts +24 -5
- package/core/line/Line.js +38 -10
- package/core/map/ConfigManager.d.ts +169 -89
- package/core/map/ConfigManager.js +157 -175
- package/core/map/MapBaseLayers.d.ts +6 -0
- package/core/map/MapBaseLayers.js +9 -0
- package/core/map/MapTools.d.ts +17 -1
- package/core/map/MapTools.js +39 -5
- package/core/point/Point.d.ts +54 -14
- package/core/point/Point.js +132 -76
- package/core/point/PointOverlay.d.ts +2 -4
- package/core/point/PointOverlay.js +2 -1
- package/core/point/PointPulseLayer.js +6 -4
- package/core/polygon/Polygon.d.ts +32 -17
- package/core/polygon/Polygon.js +87 -64
- package/core/polygon/PolygonHeatmapLayer.js +15 -2
- package/core/polygon/PolygonMaskLayer.d.ts +2 -2
- package/core/polygon/PolygonMaskLayer.js +3 -1
- package/core/polygon/PolygonStyleFactory.d.ts +4 -3
- package/core/projection/ProjectionManager.d.ts +66 -0
- package/core/projection/ProjectionManager.js +144 -0
- package/core/projection/index.d.ts +2 -0
- package/core/projection/index.js +1 -0
- package/core/select/SelectHandler.d.ts +1 -1
- package/core/vue-template-point/VueTemplatePoint.d.ts +2 -4
- package/core/vue-template-point/VueTemplatePoint.js +16 -2
- package/docs/.vitepress/config.mts +1 -0
- package/docs/Line.md +4 -4
- package/docs/MIGRATION-3.0.md +221 -0
- package/docs/Point.md +24 -6
- package/docs/Polygon.md +14 -5
- package/index.d.ts +6 -3
- package/index.js +4 -1
- package/package.json +6 -3
- package/types/base.d.ts +4 -4
- package/types/common.d.ts +8 -6
- package/types/handle.d.ts +34 -0
- package/types/handle.js +1 -0
- package/types/index.d.ts +1 -0
- package/types/line.d.ts +3 -2
- package/types/map.d.ts +1 -1
- package/types/point.d.ts +11 -3
- package/utils/ErrorHandler.d.ts +12 -0
- package/utils/ErrorHandler.js +21 -0
package/core/polygon/Polygon.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import VectorLayer from "ol/layer/Vector";
|
|
3
3
|
import VectorSource from "ol/source/Vector";
|
|
4
4
|
import GeoJSON from "ol/format/GeoJSON";
|
|
5
|
-
import { ErrorHandler } from '../../utils/ErrorHandler';
|
|
5
|
+
import { ErrorHandler, InvalidGeoJSONError, LayerNotFoundError } from '../../utils/ErrorHandler';
|
|
6
6
|
import ProjectionUtils from '../../utils/ProjectionUtils';
|
|
7
7
|
import ValidationUtils from '../../utils/ValidationUtils';
|
|
8
8
|
import { ConfigManager, MapTools } from "../map";
|
|
@@ -20,11 +20,50 @@ export default class Polygon {
|
|
|
20
20
|
* @param map OpenLayers 地图实例
|
|
21
21
|
*/
|
|
22
22
|
constructor(map) {
|
|
23
|
+
/**
|
|
24
|
+
* 由本实例创建的所有图层句柄(含 mask 子图层等)。destroyAll 时统一回收,避免地图销毁后图层泄漏。
|
|
25
|
+
*/
|
|
26
|
+
this.managedLayers = new Set();
|
|
23
27
|
if (!map) {
|
|
24
28
|
throw new Error('Map instance is required');
|
|
25
29
|
}
|
|
26
30
|
this.map = map;
|
|
27
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* 跟踪一个本实例创建的图层,便于销毁阶段统一清理。
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
trackLayer(layer) {
|
|
37
|
+
this.managedLayers.add(layer);
|
|
38
|
+
return layer;
|
|
39
|
+
}
|
|
40
|
+
/** *********************统一句柄:OL 图层*********************/
|
|
41
|
+
toLayerHandle(layer) {
|
|
42
|
+
const map = this.map;
|
|
43
|
+
const managedLayers = this.managedLayers;
|
|
44
|
+
return {
|
|
45
|
+
layer,
|
|
46
|
+
setVisible(visible) { layer.setVisible(visible); },
|
|
47
|
+
remove() {
|
|
48
|
+
managedLayers.delete(layer);
|
|
49
|
+
map.removeLayer(layer);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 销毁本实例创建的所有图层。供 MyOl.destroy 调用。
|
|
55
|
+
*/
|
|
56
|
+
destroyAll() {
|
|
57
|
+
this.managedLayers.forEach(layer => {
|
|
58
|
+
try {
|
|
59
|
+
this.map.removeLayer(layer);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// ignore
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
this.managedLayers.clear();
|
|
66
|
+
}
|
|
28
67
|
/**
|
|
29
68
|
* 添加地图边框图层
|
|
30
69
|
* @param data 图层数据,必须是有效的 GeoJSON 格式
|
|
@@ -36,13 +75,14 @@ export default class Polygon {
|
|
|
36
75
|
ValidationUtils.validateGeoJSONData(data);
|
|
37
76
|
const mergedOptions = {
|
|
38
77
|
fillColor: 'rgba(255, 255, 255, 0)',
|
|
39
|
-
...options
|
|
78
|
+
...options,
|
|
79
|
+
layerName: options?.layerName ?? 'border'
|
|
40
80
|
};
|
|
41
|
-
const
|
|
81
|
+
const handle = this.addPolygon(data, mergedOptions);
|
|
42
82
|
if (mergedOptions.mask) {
|
|
43
83
|
this.setOutLayer(data);
|
|
44
84
|
}
|
|
45
|
-
return
|
|
85
|
+
return handle;
|
|
46
86
|
}
|
|
47
87
|
/**
|
|
48
88
|
* 从URL添加地图边框图层
|
|
@@ -51,14 +91,13 @@ export default class Polygon {
|
|
|
51
91
|
* @returns 创建的图层实例
|
|
52
92
|
* @throws 当数据格式无效时抛出错误
|
|
53
93
|
*/
|
|
54
|
-
addBorderPolygonByUrl(url, options) {
|
|
94
|
+
async addBorderPolygonByUrl(url, options) {
|
|
55
95
|
const mergedOptions = {
|
|
56
|
-
layerName: 'border',
|
|
57
96
|
fillColor: 'rgba(255, 255, 255, 0)',
|
|
58
|
-
...options
|
|
97
|
+
...options,
|
|
98
|
+
layerName: options?.layerName ?? 'border'
|
|
59
99
|
};
|
|
60
|
-
|
|
61
|
-
return layer;
|
|
100
|
+
return this.addPolygonByUrl(url, mergedOptions);
|
|
62
101
|
}
|
|
63
102
|
/**
|
|
64
103
|
* 添加多边形图层
|
|
@@ -67,7 +106,8 @@ export default class Polygon {
|
|
|
67
106
|
* @returns 创建的矢量图层
|
|
68
107
|
* @throws 当数据格式无效时抛出错误
|
|
69
108
|
*/
|
|
70
|
-
|
|
109
|
+
/** *********************创建多边形图层*********************/
|
|
110
|
+
createPolygonLayer(dataJSON, options) {
|
|
71
111
|
ValidationUtils.validateGeoJSONData(dataJSON);
|
|
72
112
|
const mergedOptions = {
|
|
73
113
|
...ConfigManager.DEFAULT_POLYGON_OPTIONS,
|
|
@@ -92,7 +132,7 @@ export default class Polygon {
|
|
|
92
132
|
features = format.readFeatures(dataJSON, ProjectionUtils.getGeoJSONReadOptions(mergedOptions));
|
|
93
133
|
}
|
|
94
134
|
catch (error) {
|
|
95
|
-
throw new
|
|
135
|
+
throw new InvalidGeoJSONError(error instanceof Error ? error.message : String(error), { layerName: mergedOptions.layerName });
|
|
96
136
|
}
|
|
97
137
|
const layer = new VectorLayer({
|
|
98
138
|
properties: {
|
|
@@ -105,60 +145,26 @@ export default class Polygon {
|
|
|
105
145
|
});
|
|
106
146
|
layer.setVisible(mergedOptions.visible);
|
|
107
147
|
this.map.addLayer(layer);
|
|
148
|
+
this.trackLayer(layer);
|
|
108
149
|
// 如果需要适应视图
|
|
109
150
|
if (mergedOptions.fitView) {
|
|
110
151
|
this.fitViewToLayer(layer);
|
|
111
152
|
}
|
|
112
153
|
return layer;
|
|
113
154
|
}
|
|
114
|
-
/**
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
...options
|
|
125
|
-
};
|
|
126
|
-
// 如果指定了图层名称,先移除同名图层
|
|
127
|
-
if (mergedOptions.layerName) {
|
|
128
|
-
new MapTools(this.map).removeLayer(mergedOptions.layerName);
|
|
129
|
-
}
|
|
130
|
-
const format = new GeoJSON(ProjectionUtils.getGeoJSONReadOptions(mergedOptions));
|
|
131
|
-
// 优化:在解析 Feature 时直接注入 layerName,利用解析过程的遍历,避免解析后的二次循环
|
|
132
|
-
if (mergedOptions.layerName) {
|
|
133
|
-
const originalReadFeatureFromObject = format.readFeatureFromObject;
|
|
134
|
-
format.readFeatureFromObject = function (object, options) {
|
|
135
|
-
const feature = originalReadFeatureFromObject.call(this, object, options);
|
|
136
|
-
feature.set('layerName', mergedOptions.layerName, true); // true 表示静默设置,不触发事件
|
|
137
|
-
return feature;
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const source = new VectorSource({
|
|
141
|
-
url,
|
|
142
|
-
format
|
|
143
|
-
});
|
|
144
|
-
const layer = new VectorLayer({
|
|
145
|
-
properties: {
|
|
146
|
-
name: mergedOptions.layerName,
|
|
147
|
-
layerName: mergedOptions.layerName
|
|
148
|
-
},
|
|
149
|
-
source,
|
|
150
|
-
style: PolygonStyleFactory.createStyle(mergedOptions),
|
|
151
|
-
zIndex: mergedOptions.zIndex
|
|
152
|
-
});
|
|
153
|
-
layer.setVisible(mergedOptions.visible);
|
|
154
|
-
this.map.addLayer(layer);
|
|
155
|
-
// 如果需要适应视图
|
|
156
|
-
if (mergedOptions.fitView) {
|
|
157
|
-
source.once('featuresloadend', () => {
|
|
158
|
-
this.fitViewToLayer(layer);
|
|
159
|
-
});
|
|
155
|
+
/** *********************添加多边形图层*********************/
|
|
156
|
+
addPolygon(dataJSON, options) {
|
|
157
|
+
return this.toLayerHandle(this.createPolygonLayer(dataJSON, options));
|
|
158
|
+
}
|
|
159
|
+
/** *********************从 URL 添加多边形图层*********************/
|
|
160
|
+
async addPolygonByUrl(url, options) {
|
|
161
|
+
ValidationUtils.validateNonEmptyString(url, 'Polygon url is required');
|
|
162
|
+
const response = await fetch(url);
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`Failed to fetch polygon GeoJSON: ${response.status}`);
|
|
160
165
|
}
|
|
161
|
-
|
|
166
|
+
const json = await response.json();
|
|
167
|
+
return this.addPolygon(json, options);
|
|
162
168
|
}
|
|
163
169
|
/**
|
|
164
170
|
* 适应图层视图
|
|
@@ -181,7 +187,7 @@ export default class Polygon {
|
|
|
181
187
|
ValidationUtils.validateLayerName(layerName);
|
|
182
188
|
const layers = MapTools.getLayerByLayerName(this.map, layerName);
|
|
183
189
|
if (layers.length === 0) {
|
|
184
|
-
throw new
|
|
190
|
+
throw new LayerNotFoundError(layerName, { method: 'updateFeatureColor' });
|
|
185
191
|
}
|
|
186
192
|
const layer = layers[0];
|
|
187
193
|
if (!(layer instanceof VectorLayer)) {
|
|
@@ -211,7 +217,11 @@ export default class Polygon {
|
|
|
211
217
|
* @param options
|
|
212
218
|
*/
|
|
213
219
|
setOutLayer(data, options) {
|
|
214
|
-
|
|
220
|
+
const layer = PolygonMaskLayer.setOutLayer(this.map, data, options);
|
|
221
|
+
if (layer) {
|
|
222
|
+
this.trackLayer(layer);
|
|
223
|
+
}
|
|
224
|
+
return layer;
|
|
215
225
|
}
|
|
216
226
|
/**
|
|
217
227
|
* 添加图片图层
|
|
@@ -221,7 +231,9 @@ export default class Polygon {
|
|
|
221
231
|
* @throws 当数据格式无效时抛出错误
|
|
222
232
|
*/
|
|
223
233
|
addImageLayer(imageData, options) {
|
|
224
|
-
|
|
234
|
+
const layer = PolygonImageLayer.addImageLayer(this.map, imageData, options);
|
|
235
|
+
this.trackLayer(layer);
|
|
236
|
+
return this.toLayerHandle(layer);
|
|
225
237
|
}
|
|
226
238
|
/**
|
|
227
239
|
* 添加热力图图层
|
|
@@ -229,7 +241,11 @@ export default class Polygon {
|
|
|
229
241
|
* @param options 热力图配置
|
|
230
242
|
*/
|
|
231
243
|
addHeatmap(pointData, options) {
|
|
232
|
-
|
|
244
|
+
const layer = PolygonHeatmapLayer.addHeatmap(this.map, pointData, options);
|
|
245
|
+
if (layer) {
|
|
246
|
+
this.trackLayer(layer);
|
|
247
|
+
}
|
|
248
|
+
return this.toLayerHandle(layer);
|
|
233
249
|
}
|
|
234
250
|
/**
|
|
235
251
|
* 添加遮罩图层
|
|
@@ -239,10 +255,17 @@ export default class Polygon {
|
|
|
239
255
|
* @throws 当数据格式无效时抛出错误
|
|
240
256
|
*/
|
|
241
257
|
addMaskLayer(data, options) {
|
|
242
|
-
|
|
258
|
+
const layer = PolygonMaskLayer.addMaskLayer(this.map, data, options);
|
|
259
|
+
this.trackLayer(layer);
|
|
260
|
+
return this.toLayerHandle(layer);
|
|
243
261
|
}
|
|
244
262
|
removePolygonLayer(layerName) {
|
|
245
263
|
new MapTools(this.map).removeLayer(layerName);
|
|
246
|
-
|
|
264
|
+
// 从 managedLayers 中也移除对应记录
|
|
265
|
+
this.managedLayers.forEach(layer => {
|
|
266
|
+
if (layer.get('layerName') === layerName || layer.get('name') === layerName) {
|
|
267
|
+
this.managedLayers.delete(layer);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
247
270
|
}
|
|
248
271
|
}
|
|
@@ -3,6 +3,7 @@ import { Point } from "ol/geom";
|
|
|
3
3
|
import { Heatmap } from "ol/layer";
|
|
4
4
|
import VectorSource from "ol/source/Vector";
|
|
5
5
|
import { ConfigManager, MapTools } from "../map";
|
|
6
|
+
import { ErrorHandler } from "../../utils/ErrorHandler";
|
|
6
7
|
/**
|
|
7
8
|
* 面热力图辅助类。
|
|
8
9
|
*/
|
|
@@ -28,11 +29,23 @@ export default class PolygonHeatmapLayer {
|
|
|
28
29
|
}
|
|
29
30
|
map.addLayer(heatmapLayer);
|
|
30
31
|
const valueKey = mergedOptions.valueKey || ConfigManager.DEFAULT_HEATMAP_VALUE_KEY;
|
|
31
|
-
|
|
32
|
+
// 计算归一化最大值。当数据里没有 valueKey 或值全为非数字时,max 是 NaN/-Infinity,
|
|
33
|
+
// 会导致 weight = value/max = NaN,OL 热力图整层不渲染。
|
|
34
|
+
// 此时回退到等权重 1(让用户至少看到热力图,再去补 valueKey)。
|
|
35
|
+
const numericValues = pointData
|
|
36
|
+
.map(item => Number(item[valueKey]))
|
|
37
|
+
.filter(v => Number.isFinite(v));
|
|
38
|
+
const max = numericValues.length > 0 ? Math.max(...numericValues) : 0;
|
|
39
|
+
const useFallbackWeight = max <= 0;
|
|
40
|
+
if (useFallbackWeight) {
|
|
41
|
+
ErrorHandler.getInstance().warn(`[Heatmap] 数据中未找到有效的 "${valueKey}" 字段,已回退为等权重渲染。请通过 options.valueKey 指定正确字段。`);
|
|
42
|
+
}
|
|
32
43
|
pointData.forEach(item => {
|
|
44
|
+
const raw = Number(item[valueKey]);
|
|
45
|
+
const weight = useFallbackWeight || !Number.isFinite(raw) ? 1 : raw / max;
|
|
33
46
|
heatmapLayer.getSource().addFeature(new Feature({
|
|
34
47
|
geometry: new Point([item.lgtd, item.lttd]),
|
|
35
|
-
weight
|
|
48
|
+
weight
|
|
36
49
|
}));
|
|
37
50
|
});
|
|
38
51
|
return heatmapLayer;
|
|
@@ -10,11 +10,11 @@ import type { MapJSONData, MaskLayerOptions } from "../../types";
|
|
|
10
10
|
export default class PolygonMaskLayer {
|
|
11
11
|
static setOutLayer(map: Map, data: MapJSONData, options?: {
|
|
12
12
|
layerName?: string;
|
|
13
|
-
extent?:
|
|
13
|
+
extent?: number[];
|
|
14
14
|
fillColor?: string;
|
|
15
15
|
strokeWidth?: number;
|
|
16
16
|
strokeColor?: string;
|
|
17
17
|
zIndex?: number;
|
|
18
18
|
}): VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>>;
|
|
19
|
-
static addMaskLayer(map: Map, data:
|
|
19
|
+
static addMaskLayer(map: Map, data: MapJSONData, options?: MaskLayerOptions): VectorLayer<VectorSource>;
|
|
20
20
|
}
|
|
@@ -98,7 +98,9 @@ export default class PolygonMaskLayer {
|
|
|
98
98
|
}) : undefined
|
|
99
99
|
}),
|
|
100
100
|
opacity: mergedOptions.opacity,
|
|
101
|
-
visible: mergedOptions.visible
|
|
101
|
+
visible: mergedOptions.visible,
|
|
102
|
+
// 显式传 zIndex,否则被天地图底图(zIndex 9)盖住,用户看不到 mask
|
|
103
|
+
zIndex: mergedOptions.zIndex
|
|
102
104
|
});
|
|
103
105
|
maskLayer.set('layerName', mergedOptions.layerName);
|
|
104
106
|
map.addLayer(maskLayer);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import Feature from "ol/Feature";
|
|
1
|
+
import Feature, { type FeatureLike } from "ol/Feature";
|
|
2
|
+
import { Style } from "ol/style";
|
|
2
3
|
import type { FeatureColorUpdateOptions, PolygonOptions } from "../../types";
|
|
3
4
|
/**
|
|
4
5
|
* 面图层样式工厂。
|
|
5
6
|
*/
|
|
6
7
|
export default class PolygonStyleFactory {
|
|
7
|
-
static getFeatureText(feature:
|
|
8
|
-
static createStyle(options: PolygonOptions):
|
|
8
|
+
static getFeatureText(feature: FeatureLike, options: PolygonOptions | FeatureColorUpdateOptions): string;
|
|
9
|
+
static createStyle(options: PolygonOptions): Style | Style[] | ((feature: FeatureLike) => Style | Style[]);
|
|
9
10
|
static updateSingleFeatureColor(feature: Feature, colorObj?: {
|
|
10
11
|
[propName: string]: string;
|
|
11
12
|
}, options?: FeatureColorUpdateOptions): void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Projection as olProjProjection } from "ol/proj";
|
|
2
|
+
import type { Units } from "ol/proj/Units";
|
|
3
|
+
import type { MapInitType } from "../../types";
|
|
4
|
+
/**
|
|
5
|
+
* 项目内置 EPSG 编码。
|
|
6
|
+
*/
|
|
7
|
+
export declare const PROJECTIONS: {
|
|
8
|
+
readonly WGS84: "EPSG:4326";
|
|
9
|
+
readonly CGCS2000: "EPSG:4490";
|
|
10
|
+
readonly CGCS2000_3_DEGREE: "EPSG:4549";
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* 自定义投影注册元数据。
|
|
14
|
+
*/
|
|
15
|
+
export interface CustomProjectionRegistration {
|
|
16
|
+
/** EPSG 编码,例如 "EPSG:4528" */
|
|
17
|
+
code: string;
|
|
18
|
+
/** proj4 定义字符串 */
|
|
19
|
+
def: string;
|
|
20
|
+
/** 投影范围 [minX, minY, maxX, maxY] */
|
|
21
|
+
extent?: number[];
|
|
22
|
+
/** 世界范围 [minX, minY, maxX, maxY] */
|
|
23
|
+
worldExtent?: number[];
|
|
24
|
+
/** OL 投影单位(degrees / m / ft 等),未提供时从 proj4 定义自动推导 */
|
|
25
|
+
units?: Units;
|
|
26
|
+
}
|
|
27
|
+
export default class ProjectionManager {
|
|
28
|
+
/** 项目内置 EPSG 编码常量。 */
|
|
29
|
+
static readonly PROJECTIONS: {
|
|
30
|
+
readonly WGS84: "EPSG:4326";
|
|
31
|
+
readonly CGCS2000: "EPSG:4490";
|
|
32
|
+
readonly CGCS2000_3_DEGREE: "EPSG:4549";
|
|
33
|
+
};
|
|
34
|
+
/** 默认投影 = EPSG:4490 (CGCS2000)。 */
|
|
35
|
+
static readonly DEFAULT_PROJECTION: "EPSG:4490";
|
|
36
|
+
/**
|
|
37
|
+
* 注册内置投影 + 应用 options.projection 自定义投影。
|
|
38
|
+
*
|
|
39
|
+
* 幂等:重复调用安全。被 MyOl 构造函数和 MyOl.createView 都会调,外部一般无需直接调用。
|
|
40
|
+
*/
|
|
41
|
+
static initialize(options?: MapInitType): void;
|
|
42
|
+
/**
|
|
43
|
+
* 注册一个任意 EPSG 投影到 proj4 + OL。
|
|
44
|
+
*
|
|
45
|
+
* 这是 MyOl 之外的便利入口。units 未提供时由 proj4 自动推导。
|
|
46
|
+
*/
|
|
47
|
+
static register(registration: CustomProjectionRegistration): void;
|
|
48
|
+
/**
|
|
49
|
+
* 解析视图投影,优先复用已注册投影,避免丢失 proj4 推导的单位信息。
|
|
50
|
+
*
|
|
51
|
+
* 仅当 options 显式覆盖 extent / worldExtent / units 之一时才新建 Projection 实例。
|
|
52
|
+
*/
|
|
53
|
+
static resolveViewProjection(options: MapInitType, code: string): olProjProjection;
|
|
54
|
+
/**
|
|
55
|
+
* 缺失时注册 proj4 投影定义,避免生产构建依赖第三方模块默认副作用。
|
|
56
|
+
*/
|
|
57
|
+
private static ensureProj4Definition;
|
|
58
|
+
/**
|
|
59
|
+
* 应用用户显式提供的投影元数据。
|
|
60
|
+
*
|
|
61
|
+
* - 提供 units:以用户输入为权威,覆盖已注册投影
|
|
62
|
+
* - 未提供 units 但已注册:仅刷新 extent / worldExtent
|
|
63
|
+
* - 未提供 units 且未注册:新建 Projection(OL 会从 proj4 推导单位)
|
|
64
|
+
*/
|
|
65
|
+
private static applyCustomProjectionMetadata;
|
|
66
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 投影管理器
|
|
3
|
+
*
|
|
4
|
+
* 集中管理所有投影注册与解析逻辑,对外暴露内置常量(EPSG:4326 / 4490 / 4549)以及
|
|
5
|
+
* 自定义投影注册入口。MyOl 内部委托此类初始化投影,使用户也可以在 MyOl 实例之外
|
|
6
|
+
* 主动注册新的 EPSG。
|
|
7
|
+
*/
|
|
8
|
+
import proj4 from "proj4";
|
|
9
|
+
import { register as olProj4Register } from "ol/proj/proj4";
|
|
10
|
+
import { Projection as olProjProjection, addProjection as olProjAddProjection, get as olProjGetProjection } from "ol/proj";
|
|
11
|
+
/**
|
|
12
|
+
* 项目内置 EPSG 编码。
|
|
13
|
+
*/
|
|
14
|
+
export const PROJECTIONS = {
|
|
15
|
+
WGS84: "EPSG:4326",
|
|
16
|
+
CGCS2000: "EPSG:4490",
|
|
17
|
+
CGCS2000_3_DEGREE: "EPSG:4549"
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 内置 EPSG 的 proj4 定义字符串。
|
|
21
|
+
*/
|
|
22
|
+
const PROJECTION_DEFINITIONS = {
|
|
23
|
+
[PROJECTIONS.WGS84]: "+title=WGS 84 (long/lat) +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degrees",
|
|
24
|
+
[PROJECTIONS.CGCS2000]: "+proj=longlat +ellps=GRS80 +no_defs",
|
|
25
|
+
[PROJECTIONS.CGCS2000_3_DEGREE]: "+proj=tmerc +lat_0=0 +lon_0=120 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs"
|
|
26
|
+
};
|
|
27
|
+
class ProjectionManager {
|
|
28
|
+
/**
|
|
29
|
+
* 注册内置投影 + 应用 options.projection 自定义投影。
|
|
30
|
+
*
|
|
31
|
+
* 幂等:重复调用安全。被 MyOl 构造函数和 MyOl.createView 都会调,外部一般无需直接调用。
|
|
32
|
+
*/
|
|
33
|
+
static initialize(options) {
|
|
34
|
+
ProjectionManager.ensureProj4Definition(PROJECTIONS.WGS84, PROJECTION_DEFINITIONS[PROJECTIONS.WGS84]);
|
|
35
|
+
ProjectionManager.ensureProj4Definition(PROJECTIONS.CGCS2000, PROJECTION_DEFINITIONS[PROJECTIONS.CGCS2000]);
|
|
36
|
+
ProjectionManager.ensureProj4Definition(PROJECTIONS.CGCS2000_3_DEGREE, PROJECTION_DEFINITIONS[PROJECTIONS.CGCS2000_3_DEGREE]);
|
|
37
|
+
// 用户自定义投影的 proj4 定义优先注入
|
|
38
|
+
if (options?.projection?.code && options.projection.def) {
|
|
39
|
+
proj4.defs(options.projection.code, options.projection.def);
|
|
40
|
+
}
|
|
41
|
+
// 注入 proj4 → OpenLayers
|
|
42
|
+
olProj4Register(proj4);
|
|
43
|
+
// 注册 CGCS2000 OL Projection(带 degrees 单位)
|
|
44
|
+
olProjAddProjection(new olProjProjection({
|
|
45
|
+
code: PROJECTIONS.CGCS2000,
|
|
46
|
+
extent: [-180, -90, 180, 90],
|
|
47
|
+
worldExtent: [-180, -90, 180, 90],
|
|
48
|
+
units: "degrees"
|
|
49
|
+
}));
|
|
50
|
+
if (options?.projection?.code) {
|
|
51
|
+
ProjectionManager.applyCustomProjectionMetadata(options.projection);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 注册一个任意 EPSG 投影到 proj4 + OL。
|
|
56
|
+
*
|
|
57
|
+
* 这是 MyOl 之外的便利入口。units 未提供时由 proj4 自动推导。
|
|
58
|
+
*/
|
|
59
|
+
static register(registration) {
|
|
60
|
+
const { code, def, extent, worldExtent, units } = registration;
|
|
61
|
+
ProjectionManager.ensureProj4Definition(code, def);
|
|
62
|
+
olProj4Register(proj4);
|
|
63
|
+
ProjectionManager.applyCustomProjectionMetadata({
|
|
64
|
+
code,
|
|
65
|
+
def,
|
|
66
|
+
extent,
|
|
67
|
+
worldExtent,
|
|
68
|
+
units
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 解析视图投影,优先复用已注册投影,避免丢失 proj4 推导的单位信息。
|
|
73
|
+
*
|
|
74
|
+
* 仅当 options 显式覆盖 extent / worldExtent / units 之一时才新建 Projection 实例。
|
|
75
|
+
*/
|
|
76
|
+
static resolveViewProjection(options, code) {
|
|
77
|
+
const registeredProjection = olProjGetProjection(code);
|
|
78
|
+
if (registeredProjection
|
|
79
|
+
&& !options.projection?.extent
|
|
80
|
+
&& !options.projection?.worldExtent
|
|
81
|
+
&& !options.projection?.units) {
|
|
82
|
+
return registeredProjection;
|
|
83
|
+
}
|
|
84
|
+
return new olProjProjection({
|
|
85
|
+
code,
|
|
86
|
+
extent: options.projection?.extent
|
|
87
|
+
?? registeredProjection?.getExtent()
|
|
88
|
+
?? [-180, -90, 180, 90],
|
|
89
|
+
worldExtent: options.projection?.worldExtent
|
|
90
|
+
?? registeredProjection?.getWorldExtent()
|
|
91
|
+
?? [-180, -90, 180, 90],
|
|
92
|
+
units: options.projection?.units
|
|
93
|
+
?? registeredProjection?.getUnits()
|
|
94
|
+
?? "degrees"
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 缺失时注册 proj4 投影定义,避免生产构建依赖第三方模块默认副作用。
|
|
99
|
+
*/
|
|
100
|
+
static ensureProj4Definition(code, definition) {
|
|
101
|
+
if (!proj4.defs(code)) {
|
|
102
|
+
proj4.defs(code, definition);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 应用用户显式提供的投影元数据。
|
|
107
|
+
*
|
|
108
|
+
* - 提供 units:以用户输入为权威,覆盖已注册投影
|
|
109
|
+
* - 未提供 units 但已注册:仅刷新 extent / worldExtent
|
|
110
|
+
* - 未提供 units 且未注册:新建 Projection(OL 会从 proj4 推导单位)
|
|
111
|
+
*/
|
|
112
|
+
static applyCustomProjectionMetadata(projection) {
|
|
113
|
+
const { code, extent, worldExtent, units } = projection;
|
|
114
|
+
const registeredProjection = olProjGetProjection(code);
|
|
115
|
+
if (units) {
|
|
116
|
+
olProjAddProjection(new olProjProjection({
|
|
117
|
+
code,
|
|
118
|
+
extent: extent ?? registeredProjection?.getExtent(),
|
|
119
|
+
worldExtent: worldExtent ?? registeredProjection?.getWorldExtent(),
|
|
120
|
+
units
|
|
121
|
+
}));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (registeredProjection) {
|
|
125
|
+
if (extent)
|
|
126
|
+
registeredProjection.setExtent(extent);
|
|
127
|
+
if (worldExtent)
|
|
128
|
+
registeredProjection.setWorldExtent(worldExtent);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (extent || worldExtent) {
|
|
132
|
+
olProjAddProjection(new olProjProjection({
|
|
133
|
+
code,
|
|
134
|
+
extent,
|
|
135
|
+
worldExtent
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** 项目内置 EPSG 编码常量。 */
|
|
141
|
+
ProjectionManager.PROJECTIONS = PROJECTIONS;
|
|
142
|
+
/** 默认投影 = EPSG:4490 (CGCS2000)。 */
|
|
143
|
+
ProjectionManager.DEFAULT_PROJECTION = PROJECTIONS.CGCS2000;
|
|
144
|
+
export default ProjectionManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ProjectionManager, PROJECTIONS } from './ProjectionManager';
|
|
@@ -64,7 +64,7 @@ export default class SelectHandler {
|
|
|
64
64
|
/**
|
|
65
65
|
* 通过属性选择要素
|
|
66
66
|
*/
|
|
67
|
-
selectByProperty(propertyName: string, propertyValue:
|
|
67
|
+
selectByProperty(propertyName: string, propertyValue: unknown, options?: ProgrammaticSelectOptions): this;
|
|
68
68
|
/**
|
|
69
69
|
* 应用选择(编程式)
|
|
70
70
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Map as OLMap } from 'ol';
|
|
2
|
-
import { VueTemplatePointInstance } from '../../types';
|
|
2
|
+
import { ControlHandle, VueTemplatePointInstance } from '../../types';
|
|
3
3
|
/**
|
|
4
4
|
* Vue模板点位管理类
|
|
5
5
|
* 用于在地图上添加和管理Vue组件覆盖物
|
|
@@ -23,10 +23,8 @@ export default class VueTemplatePoint {
|
|
|
23
23
|
addVueTemplatePoint(pointDataList: any[], template: any, options?: {
|
|
24
24
|
positioning?: 'bottom-left' | 'bottom-center' | 'bottom-right' | 'center-left' | 'center-center' | 'center-right' | 'top-left' | 'top-center' | 'top-right';
|
|
25
25
|
stopEvent?: boolean;
|
|
26
|
-
}): {
|
|
27
|
-
setVisible: (visible: boolean) => void;
|
|
26
|
+
}): ControlHandle<VueTemplatePointInstance[]> & {
|
|
28
27
|
setOneVisibleByProp: (propName: string, propValue: any, visible: boolean) => void;
|
|
29
|
-
remove: () => void;
|
|
30
28
|
getPoints: () => VueTemplatePointInstance[];
|
|
31
29
|
};
|
|
32
30
|
/**
|
|
@@ -113,6 +113,7 @@ export default class VueTemplatePoint {
|
|
|
113
113
|
this.vuePoints.set(instance.id, instance);
|
|
114
114
|
});
|
|
115
115
|
return {
|
|
116
|
+
target: instances,
|
|
116
117
|
setVisible: (visible) => {
|
|
117
118
|
instances.forEach((instance) => {
|
|
118
119
|
instance.setVisible(visible);
|
|
@@ -378,9 +379,22 @@ class VueTemplatePointInstanceImpl {
|
|
|
378
379
|
throw new Error('Cannot update props on destroyed DOM point');
|
|
379
380
|
}
|
|
380
381
|
try {
|
|
381
|
-
// 重新创建Vue
|
|
382
|
+
// 重新创建Vue应用实例。
|
|
383
|
+
// 注意:createVueApp 把 options.props 当 Vue 组件的 props 声明(非传值),
|
|
384
|
+
// 根组件没有父组件传值,因此必须把每个值包成 { default: value } 才能被
|
|
385
|
+
// Vue 当 prop 默认值消费,render 里才能读到。
|
|
382
386
|
this.destroyVueApp();
|
|
383
|
-
|
|
387
|
+
const normalized = { ...this.options.props };
|
|
388
|
+
for (const key of Object.keys(newProps)) {
|
|
389
|
+
const value = newProps[key];
|
|
390
|
+
// 已经是 { type, default } 形式则原样合并,否则包装为默认值
|
|
391
|
+
const isAlreadyDeclaration = value !== null
|
|
392
|
+
&& typeof value === 'object'
|
|
393
|
+
&& (Object.prototype.hasOwnProperty.call(value, 'default')
|
|
394
|
+
|| Object.prototype.hasOwnProperty.call(value, 'type'));
|
|
395
|
+
normalized[key] = isAlreadyDeclaration ? value : { default: value };
|
|
396
|
+
}
|
|
397
|
+
this.options.props = normalized;
|
|
384
398
|
this.createVueApp();
|
|
385
399
|
}
|
|
386
400
|
catch (error) {
|
package/docs/Line.md
CHANGED
|
@@ -65,18 +65,18 @@ interface FlowLineLayerHandle {
|
|
|
65
65
|
### addLine
|
|
66
66
|
|
|
67
67
|
```typescript
|
|
68
|
-
addLine(data: MapJSONData, options
|
|
68
|
+
addLine(data: MapJSONData, options: LineOptions & { layerName: string }): LayerHandle<VectorLayer<VectorSource>>
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
添加静态线图层,返回统一 `LayerHandle`。
|
|
72
72
|
|
|
73
73
|
### addLineByUrl
|
|
74
74
|
|
|
75
75
|
```typescript
|
|
76
|
-
addLineByUrl(url: string, options
|
|
76
|
+
addLineByUrl(url: string, options: LineOptions & { layerName: string }): Promise<LayerHandle<VectorLayer<VectorSource>>>
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
从 URL
|
|
79
|
+
从 URL 加载 GeoJSON 后添加静态线图层,await 后返回完整 `LayerHandle`。
|
|
80
80
|
|
|
81
81
|
### removeLineLayer
|
|
82
82
|
|