javascript
复制代码
/**
* 创建 GeoJSON 数据处理工具
* 功能:将 GeoJSON 数据转换为 OpenLayers 要素,支持各种数据操作
*
* @returns {Object} - 返回 GeoJSON 处理方法集合
*/
const createGeoJSONProcessor = () => {
/**
* GeoJSON 格式解析器
* 用于 GeoJSON 与 OpenLayers 要素之间的相互转换
*/
const geoJSONFormat = new GeoJSON();
return {
/**
* 将 GeoJSON 对象转换为 OpenLayers 要素
* @param {Object} geoJSON - GeoJSON 对象
* @param {Object} options - 转换选项
* @param {string} options.dataProjection - 数据坐标系(默认 EPSG:4326)
* @param {string} options.featureProjection - 要素坐标系(默认 EPSG:3857)
* @returns {Feature|Array<Feature>} OpenLayers 要素或要素数组
*/
readGeoJSON(geoJSON, options = {}) {
const readOptions = {
dataProjection: options.dataProjection || 'EPSG:4326',
featureProjection: options.featureProjection || 'EPSG:3857'
};
return geoJSONFormat.readFeatures(geoJSON, readOptions);
},
/**
* 将 OpenLayers 要素转换为 GeoJSON 对象
* @param {Feature|Array<Feature>} features - OpenLayers 要素或要素数组
* @param {Object} options - 转换选项
* @param {string} options.dataProjection - 输出坐标系(默认 EPSG:4326)
* @param {string} options.featureProjection - 要素坐标系(默认 EPSG:3857)
* @returns {Object} GeoJSON 对象
*/
writeGeoJSON(features, options = {}) {
const writeOptions = {
dataProjection: options.dataProjection || 'EPSG:4326',
featureProjection: options.featureProjection || 'EPSG:3857',
rightHanded: options.rightHanded !== undefined ? options.rightHanded : true,
decimals: options.decimals || 6
};
if (Array.isArray(features)) {
return geoJSONFormat.writeFeaturesObject(features, writeOptions);
} else {
return geoJSONFormat.writeFeatureObject(features, writeOptions);
}
},
/**
* 从 URL 加载 GeoJSON 数据
* @param {string} url - GeoJSON 数据地址
* @param {Object} options - 加载选项
* @returns {Promise<Array<Feature>>} 要素数组的 Promise
*/
async loadFromURL(url, options = {}) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`加载 GeoJSON 失败: ${response.status} ${response.statusText}`);
}
const geoJSON = await response.json();
return this.readGeoJSON(geoJSON, options);
} catch (error) {
console.error('加载 GeoJSON 数据出错:', error);
throw error;
}
},
/**
* 从字符串解析 GeoJSON 数据
* @param {string} geoJSONString - GeoJSON 字符串
* @param {Object} options - 解析选项
* @returns {Feature|Array<Feature>} OpenLayers 要素或要素数组
*/
parseFromString(geoJSONString, options = {}) {
try {
const geoJSON = JSON.parse(geoJSONString);
return this.readGeoJSON(geoJSON, options);
} catch (error) {
console.error('解析 GeoJSON 字符串出错:', error);
throw error;
}
},
/**
* 提取 GeoJSON 中的坐标数据
* @param {Object} geoJSON - GeoJSON 对象
* @returns {Array} 坐标数组
*/
extractCoordinates(geoJSON) {
const coordinates = [];
// 提取不同类型几何图形的坐标
const extractFromGeometry = (geometry) => {
if (!geometry || !geometry.type || !geometry.coordinates) {
return;
}
switch (geometry.type) {
case 'Point':
coordinates.push(geometry.coordinates);
break;
case 'LineString':
case 'MultiPoint':
coordinates.push(...geometry.coordinates);
break;
case 'Polygon':
case 'MultiLineString':
geometry.coordinates.forEach(line => {
coordinates.push(...line);
});
break;
case 'MultiPolygon':
geometry.coordinates.forEach(polygon => {
polygon.forEach(ring => {
coordinates.push(...ring);
});
});
break;
case 'GeometryCollection':
if (geometry.geometries) {
geometry.geometries.forEach(geom => {
extractFromGeometry(geom);
});
}
break;
}
};
// 处理不同类型的 GeoJSON
if (geoJSON.type === 'Feature' && geoJSON.geometry) {
extractFromGeometry(geoJSON.geometry);
} else if (geoJSON.type === 'FeatureCollection' && geoJSON.features) {
geoJSON.features.forEach(feature => {
if (feature.geometry) {
extractFromGeometry(feature.geometry);
}
});
} else if (geoJSON.type && geoJSON.coordinates) {
// 直接是几何对象
extractFromGeometry(geoJSON);
}
return coordinates;
},
/**
* 合并多个 GeoJSON 对象
* @param {Array<Object>} geoJSONArray - GeoJSON 对象数组
* @returns {Object} 合并后的 GeoJSON 对象
*/
mergeGeoJSON(geoJSONArray) {
const mergedFeatures = [];
geoJSONArray.forEach(geoJSON => {
// 处理 FeatureCollection
if (geoJSON.type === 'FeatureCollection' && geoJSON.features) {
mergedFeatures.push(...geoJSON.features);
}
// 处理单个 Feature
else if (geoJSON.type === 'Feature') {
mergedFeatures.push(geoJSON);
}
// 处理几何对象,将其转换为 Feature
else if (geoJSON.type && geoJSON.coordinates) {
mergedFeatures.push({
type: 'Feature',
geometry: geoJSON,
properties: {}
});
}
});
return {
type: 'FeatureCollection',
features: mergedFeatures
};
},
/**
* 根据属性过滤 GeoJSON 特征
* @param {Object} geoJSON - GeoJSON 对象
* @param {Function} filterFn - 过滤函数,接收 properties 参数,返回布尔值
* @returns {Object} 过滤后的 GeoJSON 对象
*/
filterByProperties(geoJSON, filterFn) {
if (geoJSON.type !== 'FeatureCollection' || !geoJSON.features) {
return geoJSON;
}
const filteredFeatures = geoJSON.features.filter(feature => {
return filterFn(feature.properties || {});
});
return {
type: 'FeatureCollection',
features: filteredFeatures
};
},
/**
* 计算 GeoJSON 中的要素数量
* @param {Object} geoJSON - GeoJSON 对象
* @returns {Object} 各类型要素数量
*/
countFeatures(geoJSON) {
const counts = {
total: 0,
point: 0,
lineString: 0,
polygon: 0,
other: 0
};
// 没有要素或格式不正确
if (!geoJSON || typeof geoJSON !== 'object') {
return counts;
}
// 处理 FeatureCollection
if (geoJSON.type === 'FeatureCollection' && Array.isArray(geoJSON.features)) {
counts.total = geoJSON.features.length;
geoJSON.features.forEach(feature => {
if (feature.geometry && feature.geometry.type) {
const type = feature.geometry.type.toLowerCase();
if (type === 'point' || type === 'multipoint') {
counts.point++;
} else if (type === 'linestring' || type === 'multilinestring') {
counts.lineString++;
} else if (type === 'polygon' || type === 'multipolygon') {
counts.polygon++;
} else {
counts.other++;
}
} else {
counts.other++;
}
});
}
// 处理单个 Feature
else if (geoJSON.type === 'Feature' && geoJSON.geometry) {
counts.total = 1;
const type = geoJSON.geometry.type.toLowerCase();
if (type === 'point' || type === 'multipoint') {
counts.point = 1;
} else if (type === 'linestring' || type === 'multilinestring') {
counts.lineString = 1;
} else if (type === 'polygon' || type === 'multipolygon') {
counts.polygon = 1;
} else {
counts.other = 1;
}
}
return counts;
},
/**
* 简化 GeoJSON 几何图形(减少点的数量)
* @param {Object} geoJSON - GeoJSON 对象
* @param {number} tolerance - 简化容差
* @returns {Object} 简化后的 GeoJSON 对象
*/
simplifyGeometry(geoJSON, tolerance = 0.00001) {
// 转换为 OpenLayers 要素
const features = this.readGeoJSON(geoJSON);
// 对每个要素进行简化
features.forEach(feature => {
const geometry = feature.getGeometry();
if (geometry) {
const simplifiedGeometry = geometry.simplify(tolerance);
feature.setGeometry(simplifiedGeometry);
}
});
// 转回 GeoJSON
return this.writeGeoJSON(features);
}
};
};
// 使用示例:
const geoJSONProcessor = createGeoJSONProcessor();
// 从 URL 加载 GeoJSON 数据
geoJSONProcessor.loadFromURL('https://example.com/data.geojson')
.then(features => {
// 添加到矢量图层
vectorSource.addFeatures(features);
})
.catch(error => {
console.error('加载 GeoJSON 失败:', error);
});
// 将要素导出为 GeoJSON
const features = vectorSource.getFeatures();
const geoJSON = geoJSONProcessor.writeGeoJSON(features);
console.log(JSON.stringify(geoJSON, null, 2));
// 过滤 GeoJSON 数据
const filteredGeoJSON = geoJSONProcessor.filterByProperties(geoJSON,
properties => properties.population > 100000
);
// 简化几何图形
const simplifiedGeoJSON = geoJSONProcessor.simplifyGeometry(geoJSON, 0.0001);
javascript
复制代码
/**
* 创建空间数据生成器
* 功能:生成各种类型的模拟空间数据,用于测试和演示
*
* @returns {Object} - 返回数据生成方法集合
*/
const createSpatialDataGenerator = () => {
/**
* 生成随机坐标
* @param {Array<number>} bounds - 坐标范围 [minX, minY, maxX, maxY]
* @param {string} projection - 坐标系
* @returns {Array<number>} 随机坐标 [x, y]
*/
const generateRandomCoord = (bounds, projection = 'EPSG:4326') => {
const x = bounds[0] + Math.random() * (bounds[2] - bounds[0]);
const y = bounds[1] + Math.random() * (bounds[3] - bounds[1]);
return [x, y];
};
/**
* 生成随机颜色
* @param {boolean} includeAlpha - 是否包含透明度
* @returns {string} 颜色字符串
*/
const generateRandomColor = (includeAlpha = false) => {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
if (includeAlpha) {
const a = Math.round((Math.random() * 0.7 + 0.3) * 100) / 100; // 0.3-1.0
return `rgba(${r}, ${g}, ${b}, ${a})`;
} else {
return `rgb(${r}, ${g}, ${b})`;
}
};
return {
/**
* 生成随机点要素
* @param {number} count - 要生成的点的数量
* @param {Object} options - 生成选项
* @returns {Array<Feature>} 点要素数组
*/
generatePoints(count, options = {}) {
const points = [];
const bounds = options.bounds || [-180, -85, 180, 85]; // 默认全球范围
const projection = options.projection || 'EPSG:4326';
for (let i = 0; i < count; i++) {
const coord = generateRandomCoord(bounds, projection);
// 创建点要素
const point = new Feature({
geometry: new Point(coord),
// 添加随机属性
properties: {
id: `point-${i}`,
name: options.namePrefix ? `${options.namePrefix}-${i}` : `点 ${i}`,
value: Math.floor(Math.random() * 100),
color: generateRandomColor(),
timestamp: Date.now() - Math.floor(Math.random() * 30 * 24 * 60 * 60 * 1000) // 随机时间(30天内)
}
});
// 设置ID
point.setId(`point-${i}`);
// 添加自定义属性
if (options.customProperties) {
for (const key in options.customProperties) {
if (typeof options.customProperties[key] === 'function') {
point.set(key, options.customProperties[key](i, coord));
} else {
point.set(key, options.customProperties[key]);
}
}
}
points.push(point);
}
return points;
},
/**
* 生成随机线要素
* @param {number} count - 要生成的线的数量
* @param {Object} options - 生成选项
* @returns {Array<Feature>} 线要素数组
*/
generateLines(count, options = {}) {
const lines = [];
const bounds = options.bounds || [-180, -85, 180, 85]; // 默认全球范围
const projection = options.projection || 'EPSG:4326';
const minPoints = options.minPoints || 2;
const maxPoints = options.maxPoints || 10;
for (let i = 0; i < count; i++) {
// 确定线的点数
const numPoints = Math.floor(Math.random() * (maxPoints - minPoints + 1)) + minPoints;
const coords = [];
// 生成路径点
let lastCoord = generateRandomCoord(bounds, projection);
coords.push(lastCoord);
for (let j = 1; j < numPoints; j++) {
// 在上一个点附近生成新点,创建连续的线
const maxOffset = options.maxSegmentLength || 1; // 最大线段长度
const dx = (Math.random() * 2 - 1) * maxOffset;
const dy = (Math.random() * 2 - 1) * maxOffset;
const newCoord = [
Math.max(bounds[0], Math.min(bounds[2], lastCoord[0] + dx)),
Math.max(bounds[1], Math.min(bounds[3], lastCoord[1] + dy))
];
coords.push(newCoord);
lastCoord = newCoord;
}
// 创建线要素
const line = new Feature({
geometry: new LineString(coords),
// 添加随机属性
properties: {
id: `line-${i}`,
name: options.namePrefix ? `${options.namePrefix}-${i}` : `线 ${i}`,
length: Math.random() * 100,
color: generateRandomColor(),
width: Math.floor(Math.random() * 5) + 1
}
});
// 设置ID
line.setId(`line-${i}`);
// 添加自定义属性
if (options.customProperties) {
for (const key in options.customProperties) {
if (typeof options.customProperties[key] === 'function') {
line.set(key, options.customProperties[key](i, coords));
} else {
line.set(key, options.customProperties[key]);
}
}
}
lines.push(line);
}
return lines;
},
/**
* 生成随机多边形要素
* @param {number} count - 要生成的多边形的数量
* @param {Object} options - 生成选项
* @returns {Array<Feature>} 多边形要素数组
*/
generatePolygons(count, options = {}) {
const polygons = [];
const bounds = options.bounds || [-180, -85, 180, 85]; // 默认全球范围
const projection = options.projection || 'EPSG:4326';
const minVertices = options.minVertices || 3;
const maxVertices = options.maxVertices || 8;
for (let i = 0; i < count; i++) {
// 确定多边形的顶点数
const numVertices = Math.floor(Math.random() * (maxVertices - minVertices + 1)) + minVertices;
// 生成中心点
const center = generateRandomCoord(bounds, projection);
const radius = options.maxRadius || 1; // 最大半径
// 生成多边形顶点
const vertices = [];
for (let j = 0; j < numVertices; j++) {
const angle = (j / numVertices) * 2 * Math.PI;
// 添加一些随机性,使多边形不那么规则
const currentRadius = radius * (0.7 + Math.random() * 0.6); // 半径变化 0.7-1.3
const x = center[0] + currentRadius * Math.cos(angle);
const y = center[1] + currentRadius * Math.sin(angle);
// 确保坐标在范围内
const boundedX = Math.max(bounds[0], Math.min(bounds[2], x));
const boundedY = Math.max(bounds[1], Math.min(bounds[3], y));
vertices.push([boundedX, boundedY]);
}
// 闭合多边形
vertices.push(vertices[0]);
// 创建多边形要素
const polygon = new Feature({
geometry: new Polygon([vertices]),
// 添加随机属性
properties: {
id: `polygon-${i}`,
name: options.namePrefix ? `${options.namePrefix}-${i}` : `多边形 ${i}`,
area: Math.random() * 100,
fillColor: generateRandomColor(true),
strokeColor: generateRandomColor()
}
});
// 设置ID
polygon.setId(`polygon-${i}`);
// 添加自定义属性
if (options.customProperties) {
for (const key in options.customProperties) {
if (typeof options.customProperties[key] === 'function') {
polygon.set(key, options.customProperties[key](i, vertices));
} else {
polygon.set(key, options.customProperties[key]);
}
}
}
polygons.push(polygon);
}
return polygons;
},
/**
* 生成网格数据
* @param {Object} options - 网格选项
* @returns {Array<Feature>} 要素数组
*/
generateGrid(options = {}) {
const bounds = options.bounds || [-180, -85, 180, 85];
const cellSize = options.cellSize || 1; // 网格单元大小
const projection = options.projection || 'EPSG:4326';
const valueFunction = options.valueFunction || (() => Math.random() * 100);
const features = [];
const xSteps = Math.ceil((bounds[2] - bounds[0]) / cellSize);
const ySteps = Math.ceil((bounds[3] - bounds[1]) / cellSize);
for (let x = 0; x < xSteps; x++) {
for (let y = 0; y < ySteps; y++) {
const minX = bounds[0] + x * cellSize;
const minY = bounds[1] + y * cellSize;
const maxX = Math.min(bounds[2], minX + cellSize);
const maxY = Math.min(bounds[3], minY + cellSize);
// 生成单元格值
const value = valueFunction(x, y, minX, minY);
// 创建网格单元
const cell = new Feature({
geometry: new Polygon([[
[minX, minY],
[maxX, minY],
[maxX, maxY],
[minX, maxY],
[minX, minY]
]]),
properties: {
id: `cell-${x}-${y}`,
x: x,
y: y,
value: value,
color: options.colorFunction ? options.colorFunction(value) : generateRandomColor(true)
}
});
// 设置ID
cell.setId(`cell-${x}-${y}`);
// 添加自定义属性
if (options.customProperties) {
for (const key in options.customProperties) {
if (typeof options.customProperties[key] === 'function') {
cell.set(key, options.customProperties[key](x, y, value));
} else {
cell.set(key, options.customProperties[key]);
}
}
}
features.push(cell);
}
}
return features;
},
/**
* 生成热力图数据
* @param {number} count - 点的数量
* @param {Object} options - 生成选项
* @returns {Array<Feature>} 热力图点要素
*/
generateHeatmapData(count, options = {}) {
const points = [];
const bounds = options.bounds || [-180, -85, 180, 85];
const projection = options.projection || 'EPSG:4326';
// 创建几个热点中心
const hotspots = options.hotspots || [];
if (hotspots.length === 0) {
// 如果没有提供热点,则随机生成2-5个
const numHotspots = Math.floor(Math.random() * 4) + 2;
for (let i = 0; i < numHotspots; i++) {
hotspots.push({
center: generateRandomCoord(bounds, projection),
intensity: Math.random() * 0.8 + 0.2, // 0.2-1.0
radius: Math.random() * (options.maxRadius || 10) + (options.minRadius || 2)
});
}
}
// 根据热点生成点
for (let i = 0; i < count; i++) {
// 随机选择一个热点
const hotspotIndex = Math.floor(Math.random() * hotspots.length);
const hotspot = hotspots[hotspotIndex];
// 在热点周围生成点,距离越远概率越小
const angle = Math.random() * 2 * Math.PI;
// 使用正态分布使点在中心附近密集
const distance = Math.abs(Math.random() + Math.random() + Math.random() - 1.5) * hotspot.radius;
const x = hotspot.center[0] + distance * Math.cos(angle);
const y = hotspot.center[1] + distance * Math.sin(angle);
// 确保坐标在范围内
const boundedX = Math.max(bounds[0], Math.min(bounds[2], x));
const boundedY = Math.max(bounds[1], Math.min(bounds[3], y));
// 计算权重 - 距离中心越近权重越大
const weight = Math.max(0.1, 1 - (distance / hotspot.radius)) * hotspot.intensity;
// 创建点要素
const point = new Feature({
geometry: new Point([boundedX, boundedY]),
weight: weight, // 热力图权重
properties: {
id: `heatpoint-${i}`,
weight: weight,
hotspotId: hotspotIndex
}
});
// 设置ID
point.setId(`heatpoint-${i}`);
// 添加自定义属性
if (options.customProperties) {
for (const key in options.customProperties) {
if (typeof options.customProperties[key] === 'function') {
point.set(key, options.customProperties[key](i, [boundedX, boundedY], weight));
} else {
point.set(key, options.customProperties[key]);
}
}
}
points.push(point);
}
return points;
},
/**
* 生成聚类点数据
* @param {number} clusters - 聚类数量
* @param {number} pointsPerCluster - 每个聚类的点数
* @param {Object} options - 生成选项
* @returns {Array<Feature>} 点要素数组
*/
generateClusteredPoints(clusters, pointsPerCluster, options = {}) {
const points = [];
const bounds = options.bounds || [-180, -85, 180, 85];
const projection = options.projection || 'EPSG:4326';
// 生成聚类中心
const clusterCenters = [];
for (let i = 0; i < clusters; i++) {
clusterCenters.push({
center: generateRandomCoord(bounds, projection),
radius: options.clusterRadius || 2,
category: options.categoryFunction ? options.categoryFunction(i) : `类别${i + 1}`
});
}
// 为每个聚类生成点
let pointId = 0;
for (let i = 0; i < clusters; i++) {
const cluster = clusterCenters[i];
for (let j = 0; j < pointsPerCluster; j++) {
// 在聚类中心周围生成点
const angle = Math.random() * 2 * Math.PI;
// 距离使用平方根分布,使点分布更自然
const distance = Math.sqrt(Math.random()) * cluster.radius;
const x = cluster.center[0] + distance * Math.cos(angle);
const y = cluster.center[1] + distance * Math.sin(angle);
// 确保坐标在范围内
const boundedX = Math.max(bounds[0], Math.min(bounds[2], x));
const boundedY = Math.max(bounds[1], Math.min(bounds[3], y));
// 创建点要素
const point = new Feature({
geometry: new Point([boundedX, boundedY]),
// 添加聚类信息
properties: {
id: `cluster-point-${pointId}`,
name: `点 ${pointId}`,
clusterId: i,
category: cluster.category,
color: options.colorByCluster ?
(typeof options.colorByCluster === 'function' ?
options.colorByCluster(i) :
generateRandomColor()) :
generateRandomColor()
}
});
// 设置ID
point.setId(`cluster-point-${pointId}`);
// 添加自定义属性
if (options.customProperties) {
for (const key in options.customProperties) {
if (typeof options.customProperties[key] === 'function') {
point.set(key, options.customProperties[key](pointId, i, [boundedX, boundedY]));
} else {
point.set(key, options.customProperties[key]);
}
}
}
points.push(point);
pointId++;
}
}
return points;
},
/**
* 生成 GeoJSON 数据
* @param {Object} options - 生成选项
* @returns {Object} GeoJSON 对象
*/
generateGeoJSON(options = {}) {
const features = [];
// 生成点要素
if (options.numPoints) {
const points = this.generatePoints(options.numPoints, options.pointOptions || {});
features.push(...points);
}
// 生成线要素
if (options.numLines) {
const lines = this.generateLines(options.numLines, options.lineOptions || {});
features.push(...lines);
}
// 生成多边形要素
if (options.numPolygons) {
const polygons = this.generatePolygons(options.numPolygons, options.polygonOptions || {});
features.push(...polygons);
}
// 创建 GeoJSON 格式解析器
const geoJSONFormat = new GeoJSON();
// 将要素转换为 GeoJSON
return geoJSONFormat.writeFeaturesObject(features, {
dataProjection: options.dataProjection || 'EPSG:4326',
featureProjection: options.featureProjection || 'EPSG:3857'
});
}
};
};
// 使用示例:
const spatialDataGenerator = createSpatialDataGenerator();
// 生成随机点要素
const randomPoints = spatialDataGenerator.generatePoints(50, {
bounds: [100, 20, 120, 40], // 中国部分区域
namePrefix: '观测点'
});
// 添加到矢量图层
vectorSource.addFeatures(randomPoints);
// 生成聚类点
const clusteredPoints = spatialDataGenerator.generateClusteredPoints(5, 20, {
bounds: [100, 20, 120, 40],
colorByCluster: (clusterId) => {
const colors = ['#FF5733', '#33FF57', '#3357FF', '#F3FF33', '#FF33F3'];
return colors[clusterId % colors.length];
}
});
// 生成并导出 GeoJSON
const demoGeoJSON = spatialDataGenerator.generateGeoJSON({
numPoints: 20,
numLines: 10,
numPolygons: 5,
pointOptions: { bounds: [100, 20, 120, 40] },
lineOptions: { bounds: [100, 20, 120, 40] },
polygonOptions: { bounds: [100, 20, 120, 40] }
});
console.log(JSON.stringify(demoGeoJSON, null, 2));
javascript
复制代码
/**
* 创建要素聚类工具
* 功能:对大量点要素进行聚类显示,提高地图渲染性能
*
* @param {VectorSource} source - 矢量数据源
* @param {Object} options - 聚类选项
* @returns {Object} - 返回聚类控制方法集合
*/
const createClusteringTool = (source, options = {}) => {
// 创建聚类数据源
const clusterSource = new Cluster({
source: source,
distance: options.distance || 40,
minDistance: options.minDistance || 20,
geometryFunction: options.geometryFunction || undefined
});
// 创建聚类图层
const clusterLayer = new VectorLayer({
source: clusterSource,
style: (feature) => {
const size = feature.get('features').length;
// 单个要素使用原始样式
if (size === 1) {
return options.singlePointStyle || new Style({
image: new CircleStyle({
radius: 5,
fill: new Fill({
color: '#3399CC'
}),
stroke: new Stroke({
color: '#fff',
width: 1
})
})
});
}
// 聚类样式
return new Style({
image: new CircleStyle({
radius: Math.min(20, 10 + Math.log2(size) * 2), // 根据点数量调整大小
fill: new Fill({
color: options.clusterFillColor || '#3399CC'
}),
stroke: new Stroke({
color: options.clusterStrokeColor || '#fff',
width: options.clusterStrokeWidth || 2
})
}),
text: new Text({
text: size.toString(),
fill: new Fill({
color: options.clusterTextColor || '#fff'
}),
font: options.clusterFont || '12px Arial'
})
});
}
});
// 添加点击事件处理
let selectInteraction;
let clusterEventKey;
/**
* 初始化聚类点击事件
* @param {Map} map - OpenLayers 地图实例
*/
const initClusterClick = (map) => {
if (clusterEventKey) {
unByKey(clusterEventKey);
}
clusterEventKey = map.on('click', (evt) => {
const feature = map.forEachFeatureAtPixel(evt.pixel, (feature) => feature);
if (feature && feature.get('features')) {
const features = feature.get('features');
// 如果只有一个点,不处理
if (features.length === 1) {
return;
}
// 如果点击的是聚类,根据当前缩放级别决定行为
const zoom = map.getView().getZoom();
const maxZoom = map.getView().getMaxZoom();
if (zoom < maxZoom) {
// 放大到合适的级别以显示聚类中的点
const extent = createEmpty();
features.forEach((f) => {
const geometry = f.getGeometry();
if (geometry) {
extendExtent(extent, geometry.getExtent());
}
});
// 放大到聚类范围
map.getView().fit(extent, {
duration: 500,
padding: [50, 50, 50, 50],
maxZoom: zoom + 2 // 限制最大放大级别
});
} else {
// 已经是最大缩放级别,显示聚类中的所有点信息
if (options.onClusterClick) {
options.onClusterClick(features, evt);
}
}
}
});
};
return {
/**
* 获取聚类图层
* @returns {VectorLayer} 聚类图层
*/
getLayer() {
return clusterLayer;
},
/**
* 获取聚类数据源
* @returns {Cluster} 聚类数据源
*/
getSource() {
return clusterSource;
},
/**
* 添加到地图
* @param {Map} map - OpenLayers 地图实例
*/
addToMap(map) {
map.addLayer(clusterLayer);
initClusterClick(map);
},
/**
* 从地图移除
* @param {Map} map - OpenLayers 地图实例
*/
removeFromMap(map) {
if (clusterEventKey) {
unByKey(clusterEventKey);
clusterEventKey = null;
}
map.removeLayer(clusterLayer);
},
/**
* 设置聚类距离
* @param {number} distance - 聚类距离(像素)
*/
setDistance(distance) {
clusterSource.setDistance(distance);
},
/**
* 设置最小距离
* @param {number} minDistance - 最小聚类距离(像素)
*/
setMinDistance(minDistance) {
clusterSource.setMinDistance(minDistance);
},
/**
* 刷新聚类
*/
refresh() {
source.refresh();
clusterSource.refresh();
},
/**
* 获取指定位置的聚类信息
* @param {Array<number>} coordinate - 坐标
* @param {Map} map - OpenLayers 地图实例
* @returns {Object|null} 聚类信息或 null
*/
getClusterInfo(coordinate, map) {
const pixel = map.getPixelFromCoordinate(coordinate);
const feature = map.forEachFeatureAtPixel(pixel, (feature) => feature);
if (feature && feature.get('features')) {
const features = feature.get('features');
return {
size: features.length,
features: features,
coordinate: coordinate
};
}
return null;
}
};
};
// 使用示例:
const pointSource = new VectorSource();
// 添加大量点数据
pointSource.addFeatures(spatialDataGenerator.generatePoints(1000));
// 创建聚类工具
const clusteringTool = createClusteringTool(pointSource, {
distance: 50,
clusterFillColor: '#FF5733',
onClusterClick: (features, event) => {
console.log(`聚类包含 ${features.length} 个点`);
// 在这里可以显示聚类信息弹窗等
}
});
// 添加到地图
clusteringTool.addToMap(map);
// 调整聚类距离
clusteringTool.setDistance(80);