天天看点

las格式转osgb格式

一. 插件说明

该插件核心代码来源于github上的一个开源项目lodToolkit,侵删。该项目的作者除了完成las->osgb的转换,还有很多其它的格式转换功能。

二. las转osgb的步骤

  1. 将las读入程序中

    ① 创建一个结构体/类用于存储点云的重要信息;

    ② 读取点云的点坐标、点颜色、点强度、点总数以及坐标系等信息;

    ③ 将b读取到的信息载入到a中的结构体/类中,用于下一步操作。

  2. 将las数据分为若干瓦片

    ① 由用户指定每个瓦片的可包含的最大的点的数目tile_size;

    ② 根据tile_size将点集划分为若干瓦片。

  3. 将每个瓦片转为若干lod并写出到文件中

    ① 设置每个lod所能容纳的最大点数maxPointNumPerOneNode;

    ② 根据maxPointNumPerOneNode将一个瓦片划分成若干lod,这些lod的组织形式为一棵完全二叉树;

    ③ 写文件注意文件命名清晰易读即可。

  4. 写xml文件

    ①文件中的信息主要有两点,坐标系信息以及一个参考点。

三. 关键代码

  • 1-a 存储点信息的类
class PointCI
	{
	public:
		PointCI() : P(0,0,0), C(0, 0, 0), I(255) {}
  		osg::Vec3 P;	//点坐标
		osg::Vec3ub C;	//点颜色
		unsigned char I;	//点强度
	};
           
  • 1-b&1-c 读取点云信息并存储
//此为读取一个点的操作:点云中的信息通过laszip这个库读出,然后写入POintCI中
bool LazReader::ReadNextPoint(PointCI& pt)
		{
			if (_currentPointId < _pointCount)
			{
				// read a point
				if (laszip_read_point(_laszipReader))
				{
					/*seed::log::DumpLog(seed::log::Critical, "An error occured in reading point %I64d", _currentPointId);*/
					return false;
				}

				// init offset
				if (_currentPointId == 0)
				{
					_offsetCenter[0] = (_laszipHeader->min_x + _laszipHeader->max_x) / 2.0;
					_offsetCenter[1] = (_laszipHeader->min_y + _laszipHeader->max_y) / 2.0;
					_offsetCenter[2] = (_laszipHeader->min_z + _laszipHeader->max_z) / 2.0;

					//记录srs参考点
					_srsPoint = _offsetCenter;
				}

				//存储相对坐标
				pt.P[0] = _pointRead->X * _laszipHeader->x_scale_factor + _laszipHeader->x_offset - _offsetCenter[0];
				pt.P[1] = _pointRead->Y * _laszipHeader->y_scale_factor + _laszipHeader->y_offset - _offsetCenter[1];
				pt.P[2] = _pointRead->Z * _laszipHeader->z_scale_factor + _laszipHeader->z_offset - _offsetCenter[2];

				//存储rgb
				auto& rgb = _pointRead->rgb;
				pt.C[0] = Color8Bits(rgb[0]);
				pt.C[1] = Color8Bits(rgb[1]);
				pt.C[2] = Color8Bits(rgb[2]);
				pt.I = Color8Bits(_pointRead->intensity);

				_currentPointId++;
				return true;
			}

			return false;
		}
           
  • 2-a&2-b划分瓦片
//从点集中提取一个瓦片
		bool PointCloudToLOD::LoadPointsForOneTile(std::shared_ptr<PointVisitor> pointVisitor,
			std::vector<PointCI>& lstPoints, size_t tileSize, size_t processedPoints)
		{
			lstPoints.clear();
			size_t count = 0;

			//最后一个瓦片(未加载的点数<1.5* tileSize时,扩充瓦片大小)
			if (pointVisitor->GetNumOfPoints() - processedPoints <= 1.5 * tileSize)
			{
				tileSize = pointVisitor->GetNumOfPoints() - processedPoints;
			}

			PointCI point;
			while (count < tileSize)
			{
				int l_nFlag = pointVisitor->NextPoint(point);
				if (l_nFlag > 0)
				{
					lstPoints.push_back(point);
					++count;
					if (l_nFlag == 2)
					{
						break;
					}
				}
				else
				{
					break;
				}
			}
			if (count > 0)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
           
  • 3-a&3-b&3-c 将瓦片转为lod树并写出
//生成lod结点树
		bool TileToLOD::BuildNode(const std::vector<PointCI> *pointSet,
			std::vector<unsigned int> &pointIndex,
			osg::BoundingBox boundingBox,
			osg::BoundingBox boundingBoxLevel0,
			const std::string& saveFilePath,
			const std::string& strBlock,
			unsigned int level,
			unsigned int childNo,
			ExportMode exportMode)
		{
			// format
			std::string format;
			if (exportMode == ExportMode::OSGB)
			{
				format = ".osgb";
			}
			else
			{
				/*ccLog::Error(QString("Mode %1 is NOT support!").arg(exportMode));*/
				return false;
			}

			// filename of self, left, right
			std::string saveFileName, leftPageName, rightPageName;
			if (level == 0)
			{
				saveFileName = saveFilePath + "/" + strBlock + format;
			}
			else
			{
				char tmpSaveFileName[100];
				sprintf(tmpSaveFileName, "%s%s%d%s%d%s", strBlock.c_str(), "_L", level, "_", childNo, format.c_str());
				saveFileName.assign(tmpSaveFileName);
				saveFileName = saveFilePath + "/" + saveFileName;
			}
			char tmpLeftPageName[100], tmpRightPageName[100];
			sprintf(tmpLeftPageName, "%s%s%d%s%d%s", strBlock.c_str(), "_L", level + 1, "_", childNo * 2, format.c_str());
			leftPageName.assign(tmpLeftPageName);
			sprintf(tmpRightPageName, "%s%s%d%s%d%s", strBlock.c_str(), "_L", level + 1, "_", childNo * 2 + 1, format.c_str());
			rightPageName.assign(tmpRightPageName);

			// 递归终止条件:到达叶节点
			if (pointIndex.size() < _maxPointNumPerOneNode || level >= _maxTreeLevel)
			{
				//构建一个叶节点
				osg::ref_ptr<osg::Geode> nodeGeode = MakeNodeGeode(pointSet, pointIndex, exportMode);
				if (exportMode == ExportMode::OSGB)
				{
					//将结点数据写入文件中
					if (osgDB::writeNodeFile(*(nodeGeode.get()), saveFileName, new osgDB::ReaderWriter::Options("precision 20")) == false)
					{
					/*	ccLog::Error(QString("Write node file %1 failed!").arg(saveFileName.c_str()));*/
						return false;
					}
				}
				else
				{
					/*ccLog::Error(QString("Mode %d is NOT support!").arg(exportMode));*/
					return false;
				}
				return true;
			}

			// prepare box
			AxisInfo maxAxisInfo;
			osg::BoundingBox leftBoundingBox;
			osg::BoundingBox rightBoundingBox;

			//获取最大的轴线?,同时初始化根左右三个盒子
			maxAxisInfo = FindMaxAxis(boundingBox, leftBoundingBox, rightBoundingBox);
			double mid = (maxAxisInfo.max + maxAxisInfo.min) / 2;

			// split self, left, right
			float interval = (float)pointIndex.size() / (float)_maxPointNumPerOneNode;	//瓦片大小/每个lod结点的最大点数
			int count = -1;
			std::vector<unsigned int> selfPointSetIndex;
			std::vector<unsigned int> leftPointSetIndex;
			std::vector<unsigned int> rightPointSetIndex;
			for (int i = 0; i < pointIndex.size(); i++)
			{
				int tmp = int((float)i / interval);
				//划分根结点
				if (tmp > count && selfPointSetIndex.size() < _maxPointNumPerOneNode)
				{
					count = tmp;
					selfPointSetIndex.push_back(pointIndex[i]);
				}
				//划分左右结点
				else
				{
					PointCI tmpPoint = pointSet->at(pointIndex[i]);
					//根据轴线划分左右结点
					if (tmpPoint.P[int(maxAxisInfo.aixType)] > mid)
					{
						rightPointSetIndex.push_back(pointIndex[i]);
					}
					else
					{
						leftPointSetIndex.push_back(pointIndex[i]);
					}
				}
			}

			// export
			{
				//创建根节点
				osg::ref_ptr<osg::Group> mt(new osg::Group);
				osg::ref_ptr<osg::Geode> nodeGeode = MakeNodeGeode(pointSet, selfPointSetIndex, exportMode);
				mt->addChild(nodeGeode.get());
				selfPointSetIndex.swap(std::vector<unsigned int>());

				double rangeRatio = 4.;
				double rangeValue = boundingBoxLevel0.radius() * 2.f * _lodRatio * rangeRatio;

				//初始化左孩子
				if (leftPointSetIndex.size())
				{
					osg::ref_ptr<osg::PagedLOD>  leftPageNode = new osg::PagedLOD;
					leftPageNode->setRangeMode(osg::PagedLOD::PIXEL_SIZE_ON_SCREEN);
					leftPageNode->setFileName(0, leftPageName);
					leftPageNode->setRange(0, rangeValue, FLT_MAX);
					leftPageNode->setCenter(leftBoundingBox.center());
					leftPageNode->setRadius(leftBoundingBox.radius());
					mt->addChild(leftPageNode.get());
				}

				//初始化右孩子
				if (rightPointSetIndex.size())
				{
					osg::ref_ptr<osg::PagedLOD>  rightPageNode = new osg::PagedLOD;
					rightPageNode->setRangeMode(osg::PagedLOD::PIXEL_SIZE_ON_SCREEN);
					rightPageNode->setFileName(0, rightPageName);
					rightPageNode->setRange(0, rangeValue, FLT_MAX);
					rightPageNode->setCenter(rightBoundingBox.center());
					rightPageNode->setRadius(rightBoundingBox.radius());
					mt->addChild(rightPageNode.get());
				}

				//根结点写入文件
				if (exportMode == ExportMode::OSGB)
				{
					if (osgDB::writeNodeFile(*(mt.get()), saveFileName, new osgDB::ReaderWriter::Options("precision 20")) == false)
					{
						/*ccLog::Error(QString("Write node file %s failed!").arg(saveFileName.c_str()));*/
						return false;
					}
				}
				else
				{
					/*ccLog::Error(QString("Mode %d is NOT support!").arg(exportMode));*/
					return false;
				}
			}

			//递归处理左右孩子结点
			if (leftPointSetIndex.size())
			{
				BuildNode(pointSet, leftPointSetIndex, leftBoundingBox, boundingBoxLevel0, saveFilePath, strBlock, level + 1, childNo * 2, exportMode);
				leftPointSetIndex.swap(std::vector<unsigned int>());
			}
			if (rightPointSetIndex.size())
			{
				BuildNode(pointSet, rightPointSetIndex, rightBoundingBox, boundingBoxLevel0, saveFilePath, strBlock, level + 1, childNo * 2 + 1, exportMode);
				rightPointSetIndex.swap(std::vector<unsigned int>());
			}
			return true;
		}
	}
           

4-a 写xml文件

bool PointCloudToLOD::ExportSRS(const std::string& srs, osg::Vec3d &srsPoint, const std::string& filePath)
		{
			std::ofstream outfile(filePath);
			
			if (outfile.bad())
			{
				/*seed::log::DumpLog(seed::log::Critical, "Can NOT open file %s!", filePath.c_str());*/
				return false;
			}
			outfile << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
			outfile << "<ModelMetadata version=\"1\">\n";
			outfile << "	<SRS>"<< srs <<"</SRS>\n";
			outfile << "	<SRSOrigin>"<<std::to_string(srsPoint[0])<<"," << std::to_string(srsPoint[1])<<","<<std::to_string(srsPoint[2])<<"</SRSOrigin>\n";
			outfile << "	<Texture>\n";
			outfile << "		<ColorSource>Visible</ColorSource>\n";
			outfile << "	</Texture>\n";
			outfile << "</ModelMetadata>\n";
			if (outfile.bad())
			{
				/*seed::log::DumpLog(seed::log::Critical, "An error has occurred while writing file %s!", filePath.c_str());*/
				return false;
			}
			return true;
		}
           

四. 插件下载

百度网盘链接:https://pan.baidu.com/s/18jTQJRm2Ta0a-iMKZM9lrQ

提取码:ufhg

继续阅读