天天看點

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

繼續閱讀