@kimap/indoor-positioning-sdk
v1.0.0
Published
基于Three.js的室内定位地图SDK - Indoor Positioning Map SDK based on Three.js
Maintainers
Readme
Kimap Indoor Positioning SDK
🗺️ 专业的室内定位地图SDK - 模块化、功能完整、易于扩展
基于Three.js的企业级室内定位地图SDK,提供完整的室内定位、导航、分析功能。
🎉 最新更新
🎨 图形生成器(ShapeGenerator)⭐ v1.0.0
程序化图形生成,无需用户交互!
// 一行代码生成圆形
kimap.createCircle('id', { x: 10, y: 0, z: 10 }, 5, { color: 0xff0000 });
// 批量生成100个图形
for (let i = 0; i < 100; i++) {
kimap.createRectangle(`area-${i}`, { x: i*2, y: 0, z: 0 }, 1, 1);
}📖 详细文档: SHAPE_GENERATOR_GUIDE.md
✨ 核心特性
🎯 基础功能
- ✅ 3D地图渲染 - 加载并渲染OBJ格式的室内地图模型
- ✅ 坐标系管理 - 自定义零点坐标和坐标范围
- ✅ 实时定位 - 显示和更新当前位置,支持脉冲动画效果
- ✅ 位置标记 - 添加、移除和管理多个位置标记点
- ✅ 点击交互 - 点击地图显示弹窗信息
- ✅ 相机控制 - 支持旋转、缩放、平移等交互操作
🎨 高级功能
- ✅ 交互式绘制 - 用户在地图上逐点绘制(测距、圆形、矩形、多边形)
- ✅ 图形生成器 - 通过API直接生成图形,无需用户交互
- ✅ 轨迹追踪 - 历史轨迹显示和实时路径跟踪
- ✅ 区域选择 - 框选/圆选/多边形选择,返回区域内元素
- ✅ 路径规划 - A*、Dijkstra算法自动路径规划
- ✅ POI管理 - 兴趣点管理和搜索
- ✅ 面积计算 - 多边形、圆形面积周长计算
- ✅ 坐标转换 - 世界坐标、地图坐标、屏幕坐标互转
- ✅ 自定义元素 - 支持图片精灵和HTML元素叠加
📦 模块化架构
sdk/
├── index.ts # 主入口(KimapSDK)
├── core/ # 核心模块
│ └── MapCore.ts # 地图初始化核心
├── modules/ # 功能模块
│ ├── DrawingModule.ts # ✏️ 交互式绘制(用户点击绘制)
│ ├── ShapeGenerator.ts # 🎨 图形生成器(程序化生成)
│ ├── TrackingModule.ts # 🚶 轨迹追踪
│ └── SelectionModule.ts # 🎯 区域选择
├── utils/ # 工具类
│ ├── CoordinateUtils.ts # 📐 坐标转换
│ ├── GeometryCalculator.ts # 📊 几何计算
│ ├── PathPlanner.ts # 🗺️ 路径规划
│ └── VectorHelper.ts # 🔄 向量转换工具
└── types/ # 类型定义
├── enums.ts # 枚举
└── interfaces.ts # 接口安装
npm install three快速开始
⭐ 新功能: 图形生成器(ShapeGenerator)已上线!
除了让用户手动绘制,现在可以通过API直接生成图形:
// 直接生成圆形,无需用户点击 kimap.createCircle('fence-1', { x: 10, y: 0, z: 10 }, 5, { color: 0xff0000 });详见: 第9节 - 图形生成器
1. 基础用法
import KimapSDK from './sdk/KimapSDK';
import * as THREE from 'three';
// 创建SDK实例
const kimap = new KimapSDK({
container: document.getElementById('map-container')!,
objUrl: '/path/to/your/model.obj',
origin: { x: 0, y: 0, z: 0 }, // 零点坐标
maxX: 50, // X轴最大坐标(米)
maxY: 10, // Y轴最大坐标(高度,米)
onPositionClick: (position, object) => {
console.log('点击位置:', position);
console.log('点击对象:', object);
}
});2. 添加位置标记
// 添加静态标记点
kimap.addLocationMarker({
id: 'entrance',
position: new THREE.Vector3(5, 0, 5),
label: '入口',
color: 0x4CAF50
});
kimap.addLocationMarker({
id: 'exit',
position: new THREE.Vector3(45, 0, 45),
label: '出口',
color: 0xF44336
});3. 更新实时位置
// 模拟实时位置更新
let currentX = 0;
let currentZ = 0;
setInterval(() => {
currentX += 0.1;
currentZ += 0.05;
const position = new THREE.Vector3(currentX, 0, currentZ);
kimap.updateCurrentPosition(position);
}, 100);4. 显示弹窗
// 基础用法:显示弹窗
kimap.showPopup({
title: '当前位置',
content: '您在一楼大厅',
position: new THREE.Vector3(25, 0, 25)
});
// 人员移动时弹窗自动跟随(默认启用)
kimap.showPopup({
title: '张三',
content: '工号: 001',
position: new THREE.Vector3(25, 0, 25)
}, true); // true表示跟随目标(默认值)
// 人员移动时更新弹窗位置
setInterval(() => {
const newPosition = getUserPosition(); // 获取人员新位置
kimap.updatePopupTarget(newPosition); // 更新弹窗跟随目标
}, 1000);
// 禁用跟随功能(固定位置弹窗)
kimap.showPopup({
title: '提示',
content: '请保持安静',
position: new THREE.Vector3(25, 0, 25)
}, false); // false表示不跟随
// 手动关闭弹窗
setTimeout(() => {
kimap.hidePopup();
}, 3000);5. 坐标转换
// 地图坐标转世界坐标
const worldPos = kimap.mapToWorld(10, 0, 15);
console.log('世界坐标:', worldPos);
// 世界坐标转地图坐标
const mapPos = kimap.worldToMap(new THREE.Vector3(10, 0, 15));
console.log('地图坐标:', mapPos);6. 绘制功能
import { DrawMode } from './sdk/types/enums';
// 开始测距
kimap.startDraw(DrawMode.MEASURE, {
onDrawClick: (pos) => console.log('点击:', pos),
onDrawComplete: (points) => {
const distance = points[0].distanceTo(points[1]);
console.log(`距离: ${distance.toFixed(2)}米`);
}
});
// 绘制圆形
kimap.startDraw(DrawMode.CIRCLE);
// 绘制多边形(双击完成)
kimap.startDraw(DrawMode.POLYGON, {
onDrawComplete: (points) => {
console.log('多边形顶点:', points);
}
});
// 清除所有绘制
kimap.clearDrawings();7. 区域选择(返回内部元素)
// 框选矩形区域
const selection = kimap.selectByPolygon([
new THREE.Vector3(10, 0, 10),
new THREE.Vector3(30, 0, 10),
new THREE.Vector3(30, 0, 30),
new THREE.Vector3(10, 0, 30)
]);
console.log('选中元素:', selection.elements);
console.log('选中标记:', selection.markers);
console.log('选中POI:', selection.pois);
// 计算选择区域面积
const area = kimap.calculateSelectionArea(selection.area);
console.log(`面积: ${area.toFixed(2)} 平方米`);
// 高亮显示选中的元素
kimap.highlightSelection(selection, 0xffff00);
// 圆形选择
const circleSelection = kimap.selectByCircle(
new THREE.Vector3(20, 0, 20), // 圆心
15 // 半径
);8. 路径规划
import { PathAlgorithm } from './sdk/types/enums';
// A*算法路径规划
const pathResult = kimap.planPath({
start: new THREE.Vector3(0, 0, 0),
end: new THREE.Vector3(50, 0, 50),
algorithm: PathAlgorithm.ASTAR,
obstacles: [
// 障碍物(如墙体)
[
new THREE.Vector3(20, 0, 10),
new THREE.Vector3(30, 0, 10),
new THREE.Vector3(30, 0, 30),
new THREE.Vector3(20, 0, 30)
]
]
});
console.log('路径长度:', pathResult.distance, '米');
console.log('预计时长:', pathResult.duration, '秒');
// 显示路径
kimap.addTrack({
id: 'nav-path',
points: pathResult.path.map(pos => ({
position: pos,
timestamp: Date.now()
})),
color: 0x00ff00,
showMarkers: true
});9. 图形生成器(程序化绘制)
// ⭐ 直接生成圆形(无需用户交互)
kimap.createCircle('fence-1',
{ x: 10, y: 0, z: 10 }, // 圆心
5, // 半径(米)
{ color: 0xff0000, opacity: 0.5 }
);
// 创建矩形区域
kimap.createRectangle('office-1',
{ x: 0, y: 0, z: 0 }, // 左下角
10, // 宽度(米)
8, // 深度(米)
{ color: 0x00ff00, opacity: 0.3 }
);
// 创建多边形
const points = [
{ x: 0, y: 0, z: 0 },
{ x: 10, y: 0, z: 0 },
{ x: 10, y: 0, z: 10 },
{ x: 0, y: 0, z: 10 }
];
kimap.createPolygon('area-1', points, { color: 0x0000ff });
// 创建线条
kimap.createLine('path-1', points, {
color: 0xff0000,
linewidth: 2,
dashed: true
});
// 创建3D盒子
kimap.createBox('box-1',
{ x: 15, y: 1, z: 15 }, // 中心位置
2, 2, 2, // 宽、高、深
{ color: 0xffff00 }
);
// 管理图形
kimap.updateShape('fence-1', { color: 0x00ffff, opacity: 0.8 });
kimap.setShapeVisible('fence-1', false); // 隐藏
kimap.removeShape('fence-1'); // 移除
kimap.clearAllShapes(); // 清除所有
// 📘 详见: SHAPE_GENERATOR_GUIDE.md10. POI管理
// 添加POI
kimap.addPOI({
id: 'cafe-1',
name: '星巴克',
category: '餐饮',
position: { x: 15, y: 0, z: 20 },
tags: ['咖啡', '休闲'],
data: { phone: '123-4567', hours: '8:00-22:00' }
});
// 搜索POI
const results = kimap.searchPOI('咖啡');
console.log('搜索结果:', results);
// 导航到POI
if (results.length > 0) {
const path = kimap.planPath({
start: currentPosition,
end: results[0].position,
algorithm: PathAlgorithm.ASTAR
});
}11. 清理资源
// 销毁SDK实例
kimap.destroy();📚 详细文档
核心文档
- SDK_MODULAR_GUIDE.md - 模块化架构详细指南
- SDK_DEMO_README.md - 演示页面使用说明
绘制系统
- SDK_DRAWING_FEATURES.md - 交互式绘制功能(DrawingModule)
- SHAPE_GENERATOR_GUIDE.md - 图形生成器(ShapeGenerator)⭐
坐标与转换
- COORDINATE_TRANSFORM_GUIDE.md - 坐标转换使用指南
- COORDINATE_API_UPDATE.md - 坐标API更新说明
- API_MIGRATION_GUIDE.md - API迁移指南
其他功能
- INDOOR_POSITIONING_GUIDE.md - 室内定位功能
- KIMAP_FILE_FORMAT.md - Kimap文件格式
- RENDERING_CONSISTENCY.md - 渲染一致性保证
API 文档
配置选项 (KimapConfig)
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| container | HTMLElement | ✅ | 地图容器DOM元素 |
| objUrl | string | ✅ | OBJ模型文件URL |
| origin | {x, y, z} | ✅ | 坐标系零点坐标 |
| maxX | number | ✅ | X轴最大坐标(米) |
| maxY | number | ✅ | Y轴最大坐标/高度(米) |
| onPositionClick | function | ❌ | 点击位置回调函数 |
主要方法
addLocationMarker(marker: LocationMarker)
添加位置标记点。
参数:
marker.id(string) - 标记唯一IDmarker.position(Vector3) - 标记位置marker.label(string, 可选) - 标记文字标签marker.color(number, 可选) - 标记颜色(十六进制)
示例:
kimap.addLocationMarker({
id: 'poi-001',
position: { x: 25, y: 0, z: 25 },
label: 'POI点',
color: 0xFF5722
});removeLocationMarker(id: string)
移除指定ID的位置标记。
示例:
kimap.removeLocationMarker('poi-001');updateCurrentPosition(position: Vector3)
更新当前实时位置(带脉冲动画效果)。
示例:
kimap.updateCurrentPosition(new THREE.Vector3(15, 0, 25));showPopup(info: PopupInfo, follow?: boolean)
显示信息弹窗。
参数:
info.title(string) - 弹窗标题info.content(string) - 弹窗内容info.position(Vector3) - 弹窗显示位置follow(boolean, 可选) - 是否跟随目标位置,默认为true
示例:
// 跟随模式(默认)
kimap.showPopup({
title: '张三',
content: '工号: 001<br>部门: 研发部',
position: new THREE.Vector3(30, 0, 40)
}, true);
// 固定位置模式
kimap.showPopup({
title: '会议室A',
content: '3层东侧,可容纳20人',
position: new THREE.Vector3(30, 0, 40)
}, false);updatePopupTarget(position: Vector3D)
更新弹窗跟随的目标位置。用于人员移动时实时更新弹窗位置。
参数:
position(Vector3D) - 新的目标位置
示例:
// 显示人员弹窗
kimap.showPopup({
title: '张三',
content: '工号: 001',
position: personPosition
});
// 人员移动时更新弹窗位置
setInterval(() => {
const newPos = getPersonPosition();
kimap.updatePopupTarget(newPos);
}, 100);hidePopup()
隐藏当前弹窗并停止跟随。
mapToWorld(x, y, z)
将地图坐标转换为世界坐标。
返回: THREE.Vector3
worldToMap(position)
将世界坐标转换为地图坐标。
返回: {x: number, y: number, z: number}
getCoordinateSystem()
获取当前坐标系信息。
返回: {origin: Vector3, maxX: number, maxY: number}
destroy()
销毁SDK实例,释放资源。
完整示例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Kimap SDK Demo</title>
<style>
#map-container {
width: 100vw;
height: 100vh;
position: relative;
}
.control-panel {
position: absolute;
top: 20px;
left: 20px;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 100;
}
</style>
</head>
<body>
<div id="map-container">
<div class="control-panel">
<h3>室内定位演示</h3>
<button onclick="startTracking()">开始定位</button>
<button onclick="stopTracking()">停止定位</button>
<button onclick="addMarker()">添加标记</button>
</div>
</div>
<script type="module">
import KimapSDK from './sdk/KimapSDK.js';
import * as THREE from 'three';
// 初始化SDK
const kimap = new KimapSDK({
container: document.getElementById('map-container'),
objUrl: './models/building.obj',
origin: { x: 0, y: 0, z: 0 },
maxX: 100,
maxY: 15,
onPositionClick: (position, object) => {
if (object && object.userData.type === 'locationMarker') {
kimap.showPopup({
title: object.userData.label,
content: `坐标: (${position.x.toFixed(2)}, ${position.z.toFixed(2)})`,
position: position
});
}
}
});
// 添加初始标记
kimap.addLocationMarker({
id: 'start',
position: new THREE.Vector3(10, 0, 10),
label: '起点',
color: 0x4CAF50
});
kimap.addLocationMarker({
id: 'end',
position: new THREE.Vector3(90, 0, 90),
label: '终点',
color: 0xF44336
});
// 定位追踪
let trackingInterval = null;
let currentPos = { x: 10, z: 10 };
window.startTracking = function() {
if (trackingInterval) return;
trackingInterval = setInterval(() => {
// 模拟移动
currentPos.x += Math.random() * 2 - 1;
currentPos.z += Math.random() * 2 - 1;
// 限制在范围内
currentPos.x = Math.max(0, Math.min(100, currentPos.x));
currentPos.z = Math.max(0, Math.min(100, currentPos.z));
kimap.updateCurrentPosition(
new THREE.Vector3(currentPos.x, 0, currentPos.z)
);
}, 100);
};
window.stopTracking = function() {
if (trackingInterval) {
clearInterval(trackingInterval);
trackingInterval = null;
}
};
window.addMarker = function() {
const id = 'marker-' + Date.now();
const x = Math.random() * 100;
const z = Math.random() * 100;
kimap.addLocationMarker({
id: id,
position: new THREE.Vector3(x, 0, z),
label: `标记-${id.substr(-4)}`,
color: 0x2196F3
});
};
// 清理
window.addEventListener('beforeunload', () => {
kimap.destroy();
});
</script>
</body>
</html>浏览器兼容性
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
需要支持WebGL 2.0。
许可证
MIT License
技术支持
如有问题请联系技术支持或查看源码注释。
