RTree 3D 空间查询
给定3D空间的包围盒,查询与包围盒符合特定空间关系的几何对象。
本文使用的是LibSpatialIndex
开源库。
自定义数据从 索引(int tileid) 到几何的映射
#ifndef RTREE_BUILDER_3D_HPP
#define RTREE_BUILDER_3D_HPP
#include <iostream>
#include <spatialindex/SpatialIndex.h>
/*3D 空间包围盒*/
struct mbb_3d {
double low[3];
double high[3];
};
/*结果集存储集合*/
class MyVisitor : public SpatialIndex::IVisitor
{
public:
std::vector<SpatialIndex::id_type> matches; // contains ids of matching objects
public:
MyVisitor() {}
~MyVisitor() {
matches.clear();
}
void visitNode(const SpatialIndex::INode& n) {}
void visitData(std::string &s) {}
void visitData(const SpatialIndex::IData& d)
{
matches.push_back(d.getIdentifier());
}
void visitData(std::vector<const SpatialIndex::IData*>& v) {}
void visitData(std::vector<uint32_t>& v){}
};
/* 自定义数据流从索引(tileid)到 geometry (box3D)的映射 */
class CustomDataStream : public SpatialIndex::IDataStream
{
public:
CustomDataStream(std::vector<struct mbb_3d *> *inputdata ) : m_pNext(0), len(0), m_id(0)
{
if (inputdata->empty())
throw Tools::IllegalArgumentException("Input size is ZERO.");
shapes = inputdata;
len = inputdata->size();
iter = shapes->begin();
readNextEntry();
}
virtual ~CustomDataStream()
{
if (m_pNext != 0) delete m_pNext;
}
virtual SpatialIndex::IData* getNext()
{
if (m_pNext == 0) return 0;
SpatialIndex::RTree::Data* ret = m_pNext;
m_pNext = 0;
readNextEntry();
return ret;
}
virtual bool hasNext()
{
return (m_pNext != 0);
}
virtual uint32_t size()
{
return len;
//throw Tools::NotSupportedException("Operation not supported.");
}
virtual void rewind()
{
if (m_pNext != 0)
{
delete m_pNext;
m_pNext = 0;
}
m_id = 0;
iter = shapes->begin();
readNextEntry();
}
void readNextEntry()
{
if (iter != shapes->end())
{
//std::cerr<< "readNextEntry m_id == " << m_id << std::endl;
SpatialIndex::Region r((*iter)->low, (*iter)->high, 3);
m_pNext = new SpatialIndex::RTree::Data(sizeof(double), reinterpret_cast<uint8_t*>((*iter)->low), r, m_id);
iter++;
m_id++;
}
}
SpatialIndex::RTree::Data* m_pNext;
std::vector<struct mbb_3d*> * shapes;
std::vector<struct mbb_3d*>::iterator iter;
int len;
SpatialIndex::id_type m_id;
};
class GEOSDataStreamFileTile : public SpatialIndex::IDataStream
{
public:
std::map<SpatialIndex::id_type, std::string> *id_tiles;
public:
GEOSDataStreamFileTile(char *input_file,
std::map<SpatialIndex::id_type, std::string> *id_tiles_ptr) : m_pNext(0)
{
m_fin.open(input_file);
id_tiles = id_tiles_ptr;
m_id = 0;
if (! m_fin)
throw Tools::IllegalArgumentException("Input file not found.");
readNextEntry();
}
virtual ~GEOSDataStreamFileTile()
{
if (m_pNext != 0) delete m_pNext;
}
virtual SpatialIndex::IData* getNext()
{
if (m_pNext == 0) return 0;
SpatialIndex::RTree::Data* ret = m_pNext;
m_pNext = 0;
readNextEntry();
return ret;
}
virtual bool hasNext()
{
return (m_pNext != 0);
}
virtual uint32_t size()
{
throw Tools::NotSupportedException("Operation not supported.");
}
virtual void rewind()
{
if (m_pNext != 0)
{
delete m_pNext;
m_pNext = 0;
}
m_fin.seekg(0, std::ios::beg);
readNextEntry();
m_id = 0;
}
void readNextEntry()
{
std::string tile_id;
double low[3], high[3];
m_fin >> tile_id >> low[0] >> low[1] >> low[2] >> high[0] >> high[1] >> high[2];
/* store tile_id */
if (m_fin.good())
{
SpatialIndex::Region r(low, high, 3);
m_pNext = new SpatialIndex::RTree::Data(sizeof(double), reinterpret_cast<uint8_t*>(low), r, m_id);
/* Use spatialproc struct */
//stop.id_tiles[m_id] = tile_id;
(*id_tiles)[m_id] = tile_id;
}
m_id++;
}
SpatialIndex::id_type m_id;
std::ifstream m_fin;
SpatialIndex::RTree::Data* m_pNext;
};
#endif
构建空间索引
#define FillFactor 0.9
#define IndexCapacity 10
#define LeafCapacity 50
id_type indexIdentifier;
GEOSDataStreamFileTile stream(cachefilename, id_tiles);
IStorageManager* storage = StorageManager::createNewMemoryStorageManager();
ISpatialIndex* spidx = RTree::createAndBulkLoadNewRTree(RTree::BLM_STR, stream, *storage,
FillFactor,
IndexCapacity,
LeafCapacity,
3,
RTree::RV_RSTAR, indexIdentifier);
进行空间查询
ISpatialIndex* spidx;
double low[3];
double high[3];
Region r(low, high, 3);
spidx->intersectsWithQuery(r, vis);
//spidx->containsWhatQuery(r, vis);
for (uint32_t i = 0; i < vis.matches.size(); i++) {
cout <<"id:" << (*id_tiles)[vis.matches[i]] << endl;//tile id
}
vis.matches.clear();
构建项目
# exe project
add_executable(SpatialIndexDemo "")
# 自定义函数获取头文件和源文件
cplusplus_glob_files(SPATIAL_INDEX_DEMO_SOURCE src/*.cpp)
cplusplus_glob_files(SPATIAL_INDEX_DEMO_HEADERS src/*.h src/*.hpp)
cplusplus_glob_files(SPATIAL_INDEX_DEMO_PUBLIC_HEADERS include/*.h)
# 设置预处理定义
target_compile_definitions(
SpatialIndexDemo
PRIVATE
SIDX_DLL_EXPORT=1
DATA_FILE=\"${PROJECT_SOURCE_DIR}/Data/data.txt\"
)
# 设置源文件和头文件
target_sources(
SpatialIndexDemo
PRIVATE
${SPATIAL_INDEX_DEMO_SOURCE}
${SPATIAL_INDEX_DEMO_HEADERS}
PUBLIC
${SPATIAL_INDEX_DEMO_PUBLIC_HEADERS}
)
# 包含头文件
target_include_directories(
SpatialIndexDemo
SYSTEM PUBLIC
${CMAKE_CURRENT_LIST_DIR}/include
${CMAKE_INSTALL_PREFIX}/include
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src/
)
# 链接库
target_link_libraries(SpatialIndexDemo
PUBLIC
spatialindex
)
测试数据
id lowx lowy lowz highx highy highz
模拟 {id,geometry}
空间查询结果
输入:lowx,lowy,lowz,highx,highy,highz
;
输出:符合对应空间关系几何对象的id。
intersectsWithQuery
: 相交查询
containsWhatQuery
:包含查询