两个经纬度之间补点

该博客介绍了如何在给定两点经纬度坐标的情况下,计算这两点之间的中间点坐标以及根据步长补足多个点的坐标。提供了一种计算两点间距离的算法,并实现了在地图上标记这些点的功能。适用于地理信息系统或地图应用开发。

方案一: 获取两点坐标中间某个点的坐标

在这里插入图片描述

/**
 * 获取两点坐标间 某一点的坐标
 * 已知a、b两点的坐标 a(x1,y1),b(x2,y2), c到a的距离r 。求c点的坐标 c(cx,cy)。
 * 求 cy 可以根据d / a到b距离 = a到c垂直距离 / a 到 b的垂直距离。
 * cy = (d*(y2-y1))/r + y1;
 * cx = (d*(x2-x1))/r + x1;
 * @param a.lng x1
 * @param a.lat y1
 * @param b.lng x2
 * @param b.lat y2
 * @param r     a点到c的距离r
 * @param d     a点到b的距离d
 * @return {Object | null}
 */

function getOneBetweenTwoPoint(a, b, d, r) {
    if (!(a && b && d && r)) {
        return null;
    }
    let cx = (r * (b.lng - a.lng)) / d + a.lng;
    let cy = (r * (b.lat - a.lat)) / d + a.lat;
    return { lng: cx, lat: cy };
}

/**
 * 计算两个经纬度坐标点之间的距离
 * @param  {object} sp 起点坐标点
 * @param  {object} ep 终点坐标点
 * @return {number} 计算后得出两点之间的距离
 */
function getDistance(sp, ep) {
    let getRad = (d) => (d * Math.PI) / 180.0; //经纬度转换成三角函数中度分表形式
    let radLat1 = getRad(sp.lat);
    let radLat2 = getRad(ep.lat);
    let a = radLat1 - radLat2;
    let b = getRad(sp.lng) - getRad(ep.lng);
    let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
    s = s * 6378137; // 地球半径(米);
    return s;
}


// 使用示例补中间点(百度地图)
let aPoint = { lng: 116.399957, lat: 40.000328 }; //起点
let bPoint = { lng: 116.511778, lat: 39.841846 }; //终点
let distance = getDistance(aPoint, bPoint); //a点与b点的距离
let range = distance / 2; //步长(总距离除以2是取两个点的中间步长)

let aMarker = new BMap.Marker(new BMap.Point(aPoint.lng , aPoint.lat));
let bMarker = new BMap.Marker(new BMap.Point(bPoint.lng , bPoint.lat));
let cMarker = getOneBetweenTwoPoint(aPoint, bPoint, distance, range);

map.addOverlay(aMarker);
map.addOverlay(bMarker);
map.addOverlay(cMarker);

方案二:获取两点坐标间补点的坐标集合

在这里插入图片描述

实现思路

1.计算起点与终点之间的距离
2.根据距离除以步长(每多少米补一个点)计算出需要补点的总数
3.用终点经纬度减去起点经纬度得到经纬度差
4.用经纬度差除以补点的总数得到每步的经纬度差
5.循环补点的总数,每次在起点经纬度的基础上加上经纬度差乘以总数

/**
 * 计算两个经纬度坐标点之间的距离
 * @param  {Object} sp 起点坐标点
 * @param  {Object} ep 终点坐标点
 * @return {Number} 计算后得出两点之间的距离
 */
function getDistance(sp, ep) {
    let getRad = (d) => (d * Math.PI) / 180.0; //经纬度转换成三角函数中度分表形式
    let radLat1 = getRad(sp.lat);
    let radLat2 = getRad(ep.lat);
    let a = radLat1 - radLat2;
    let b = getRad(sp.lng) - getRad(ep.lng);
    let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
    s = s * 6378137; // 地球半径(米);
    return s;
}

/**
 * 根据总距离和步长在两个经纬度坐标点之间补点
 * @param  {Object} sp 起点坐标点
 * @param  {Object} ep 终点坐标点
 * @param  {Number} d 总距离(米)
 * @param  {Number} r 步长(米)
 * @param  {Boolean} c 输出是否包含起终点
 * @return {Array} 补点后的坐标数组(包含起点和终点)
 */
function getFillPoints(sp, ep, d, r, c) {
	if (!(sp && ep && d && r)) {
        return [];
    }
    
    let lngDiff = ep.lng - sp.lng; //起点与终端经度差
    let latDiff = ep.lat - sp.lat; //起点与终端纬度差
    let n = Math.ceil(d / r); //补点的总数
    let a = lngDiff / n; //每步的经度差
    let b = latDiff / n; //每步的纬度差
    let points = []; //坐标数组

    for (let i = 1; i < n; i++) {
        let lng = sp.lng + a * i;
        let lat = sp.lat + b * i;
        points.push({ lng, lat });
    }
    if(c) {
    	points.unshift(sp); //添加起点
    	points.push(ep); //添加终点
    }
    return points;
}


// 使用示例(百度地图)
let startPoint = { lng: 116.399957, lat: 40.000328 }; //起点
let endPoint = { lng: 116.511778, lat: 39.841846 }; //终点
let distance = getDistance(startPoint, endPoint); //起点与终点的距离
let range = 1000; //步长
let containBoth = true; //包含两端

let allPoints = getFillPoints(startPoint, endPoint, distance, range, containBoth);

allPoints.forEach((point) => {
    let marker = new BMap.Marker(new BMap.Point(point.lng, point.lat));
    map.addOverlay(marker);
});
在 **包含等高线数据的 DXF 文件**中,每条等高线通常是一条独立的多段线(`LWPOLYLINE` 或 `POLYLINE`),表示某个特定高程值的地形轮廓。你的目标是: > ✅ **提取所有等高线的边界,并将它们构建成多个多边形(Polygon)用于 Cesium 可视化** 但要注意:**等高线本身不是“填充区域”**,而是**曲线集合**。要生成“多边形”,你需要明确你是想: - 把每条等高线作为**折线(Polyline)绘制** - 还是将相邻等高线之间围成的区域构造成**面状多边形(如分级设色图)** --- ## 🎯 本回答目标 ✅ 提供完整方案: 1. 解析 DXF 中的所有等高线 2. 获取每条等高线的顶坐标 3. 将其转换为地理坐标 4. 在 Cesium 中渲染为: - 方式 A:多条彩色折线(显示不同高度) - 方式 B:构建闭合多边形(填充高程区间) --- ### ✅ 步骤 1:解析 DXF 等高线数据(使用 `dxf-parser`) ```html <script src="https://unpkg.com/dxf-parser@0.2.5/dist/dxf-parser.min.js"></script> ``` ```javascript async function loadDxfContourLines() { const response = await fetch('contours.dxf'); // 替换为你的文件路径 const dxfText = await response.text(); const parser = new window.DxfParser(); const dxf = parser.parse(dxfText); const contours = []; // 存储所有等高线 { points: [[x,y],...], elevation: z } for (let entity of dxf.entities) { if (entity.type === 'LWPOLYLINE' || entity.type === 'POLYLINE') { let points = []; if (entity.vertices) { points = entity.vertices.map(v => [v.x, v.y]); } // 判断 Z 值来源:可以从 layer 名称、entity.elevation 或 group code 推断 let elevation = null; // 方法一:从 Layer 名称提取高程(常见做法) const layerName = dxf.tables.layer.byId[entity.layer]?.name || ''; const match = layerName.match(/(\d+(\.\d+)?)/); // 匹配数字 if (match) { elevation = parseFloat(match[0]); } // 方法二:如果没有 layer 信息,尝试用 entity 的 elevation 属性 if (!elevation && typeof entity.elevation === 'number') { elevation = entity.elevation; } // 如果仍无法获取,跳过或设默认值 if (!elevation || points.length < 2) continue; contours.push({ points, elevation }); } } return contours; // 返回所有等高线数据 } ``` --- ### ✅ 步骤 2:映射到地理坐标(局部 → WGS84 经纬度) ```js function dxfToCartographic(points, originLonLat = [116.3, 39.9], scale = 1e-5) { return points.map(([x, y]) => { const lon = originLonLat[0] + x * scale; const lat = originLonLat[1] + y * scale; return Cesium.Cartesian3.fromDegrees(lon, lat, 0); // 初始高度为 0,后续可替换 }); } ``` > 🔔 `scale` 需根据 DXF 单位调整(例如 1 米 = 1e-5 度 ≈ 1.1 米) --- ### ✅ 方式 A:渲染为彩色等高线(Polyline) ```js const viewer = new Cesium.Viewer('cesiumContainer'); loadDxfContourLines().then(contours => { contours.forEach(contour => { const { points, elevation } = contour; const positions = dxfToCartographic(points); viewer.entities.add({ polyline: { positions: positions, width: 2, material: new Cesium.PolylineColorMaterialProperty({ color: getColorByElevation(elevation) // 按高程上色 }), clampToGround: true }, name: `Contour ${elevation}m` }); }); }); // 根据高程返回颜色(示例) function getColorByElevation(z) { const minZ = 0, maxZ = 1000; const ratio = (z - minZ) / (maxZ - minZ); return Cesium.Color.interpolateWith( Cesium.Color.BLUE, Cesium.Color.RED, Math.min(Math.max(ratio, 0), 1) ); } ``` --- ### ✅ 方式 B:生成闭合多边形(填充等高距之间的区域) 如果你想把两个相邻等高线之间的区域变成“面”来填充颜色(比如做地形分层设色),可以这样做: #### 思路: - 按高程排序所有等高线 - 对每一对相邻高程区间 `[z1, z2]`,外环取较高值等高线,内环取较低值等高线(可选) - 构造 `PolygonHierarchy` 实现“环形填充” ```js function createContourPolygons(contours) { // 按高程排序 const sorted = contours.sort((a, b) => a.elevation - b.elevation); for (let i = 0; i < sorted.length - 1; i++) { const lower = sorted[i]; const upper = sorted[i + 1]; // 定义区间中值颜色 const midElev = (lower.elevation + upper.elevation) / 2; // 转换为地理坐标 const outerRing = dxfToCartographic(upper.points); const innerRing = dxfToCartographic(lower.points); // 创建多边形:外环为高处,内环为空洞(可选) viewer.entities.add({ polygon: { hierarchy: new Cesium.PolygonHierarchy(outerRing, [innerRing]), material: getColorByElevation(midElev).withAlpha(0.4), clampToGround: true }, name: `Elevation Band ${lower.elevation} - ${upper.elevation}m` }); } } // 使用 loadDxfContourLines().then(createContourPolygons); ``` 📌 注意: - 并非所有等高线都嵌套!需判断内外关系(可通过是否在多边形内部判断) - 复杂地形建议使用 GIS 工具预处理(如 QGIS → 导出为 GeoJSON) --- ### ✅ 充:自动识别闭合等高线 确保每条线是闭合的(首尾相近): ```js function makeClosed(points) { const first = points[0]; const last = points[points.length - 1]; const dist = Math.hypot(last[0] - first[0], last[1] - first[1]); if (dist > 1e-3) { // 不闭合则上 points.push([...first]); } return points; } ``` --- ### ⚠️ 常见问题与建议 | 问题 | 解决方案 | |------|----------| | DXF 中无高程信息 | 使用图层名、块名或外部元数据匹配 | | 多个岛屿/空洞 | 分析拓扑结构,使用 `turf.js` 判断包含关系 | | 坐标偏移严重 | 使用控制进行仿射变换配准(平移+旋转+缩放) | | 数据量大卡顿 | 改用 Primitive API 或 3D Tiles 批量渲染 | --- ### ✅ 推荐工具链(进阶) | 工具 | 用途 | |------|------| | **QGIS** | 将 DXF 转为 GeoJSON,添加高程字段,导出规范数据 | | **Turf.js** | 分析多边形包含、裁剪、缓冲区等操作 | | **CesiumJS + 3D Tiles** | 海量等高线可视化最佳实践 | ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值