2018.03.19
//////////////////////////////////////////////////////////////
// 该头文件提供了将 osm数据 按照分块,列式存储的方式序列化的基本方法//
//////////////////////////////////////////////////////////////
#include "osmpbfreader.h" // 解析osmpbf 的头文件
#include <fstream>
#include <iostream>
#include <iomanip>
#include <zlib.h> // pbf blobs中使用到了zlib压缩
#include <stdint.h> // 定义了几种扩展的整数类型和宏
#include <netinet/in.h> // 提供了network-byte-order的转换函数
#include <cstdlib> // for exit()
#include <cstring>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
typedef uint8_t byte;
const int max_block_size = 64*1024*1024;
const int max_numberbuf_size = 1024*1024; // 数值型缓冲区的最大空间 100KB
const int max_stringbuf_size = 32 * 1024 * 1024; //存放字符串的最大空间 32MB
const int group_size = 10000; // 一组中包含同类的、不大于10000个OSM对象
// ----------------------------------------------------------------
// module number_buf
// 这个模块提供将 整型数 以 varint编码方式 写进缓冲区的函数
// 注意: 这些写入操作并不会使缓冲区当前的写入位置向后移动,
// 所以如果需要 追加数据 ,则每次执行本操作后
// 要手动将 缓冲当前写入位置后移 写入的字节数
static inline int uint32_buf(byte* p, uint32_t v){
// 将 无符号32位整数 序列化到 目标p所指的内存地址的起始位置中
// 参数: - p :缓冲区当前写入位置的指针
// - v : 无符号32位整数
// 返回: 写入的字节数
byte* p0;
uint32_t frac;
p0= p;
frac= v&0x7f;
if(frac==v) { // just one byte
*p++= frac;
return 1;
}
do {
*p++= frac|0x80;
v>>= 7;
frac= v&0x7f;
} while(frac!=v);
*p++= frac;
return p-p0;
}
static inline int sint32_buf(byte* p,int32_t v) {
// 将 有符号32位整数 序列化到 目标p所指的内存地址的起始位置中
byte* p0;
uint32_t u;
uint32_t frac;
p0= p;
if(v<0) {
u= -v;
u= (u<<1)-1;
}
else
u= v<<1;
frac= u&0x7f;
if(frac==u) {
*p++= frac;
return 1;
}
do {
*p++= frac|0x80;
u>>= 7;
frac= u&0x7f;
} while(frac!=u);
*p++= frac;
return p-p0;
}
static inline int sint64_buf(byte* p, int64_t v) {
// 将 有符号64位整数 序列化到 目标p所指的内存地址的起始位置中
byte* p0;
uint64_t u;
uint32_t frac;
p0= p;
if(v<0) {
u= -v;
u= (u<<1)-1;
}
else
u= v<<1;
frac= u&0x7f;
if(frac==u) {
*p++= frac;
return 1;
}
do {
*p++= frac|0x80;
u>>= 7;
frac= u&0x7f;
} while(frac!=u);
*p++= frac;
return p-p0;
}
// module number_buf
// ----------------------------------------------------------------
class OSMBuffer{
private:
std::ofstream file;
// 主缓冲区
byte* Block_Buf = NULL;
byte* block_bufp = NULL;
// 为所有OSM属性 定义缓冲区,及操作函数
byte* Id_Buf = NULL; // id
byte* id_bufp = NULL; // ID缓冲区的写入指针当前的位置
// ------------ lon/lat -----------------------
byte* Lon_Buf = NULL; // longitude
byte* lon_bufp = NULL; // 写入指针当前位置
byte* Lat_Buf = NULL; // latitude
byte* lat_bufp = NULL; // 写入指针当前位置
// ----------- tags: key/val(字符串型) ------------------
byte* KeyVal_Buf = NULL; // key/val共用一块缓冲区
byte* keyval_bufp = NULL;
// 字符串型数据都要配有 索引缓冲区
byte* KeyValId_Buf = NULL; // key/val的索引缓冲区
byte* keyvalid_bufp = NULL;
// ------------ info --------------------------
byte* Version_Buf = NULL; // version
byte* version_bufp = NULL;
byte* Time_Buf = NULL; // timestamp
byte* time_bufp = NULL;
byte* Cset_Buf = NULL; //changeset
byte* cset_bufp = NULL;
byte* Uid_Buf = NULL; //user_id
byte* uid_bufp = NULL;
byte* Uname_Buf = NULL; // user_name
byte* uname_bufp = NULL;
// ------------ 参考成员的ID,way和relation可共用 --------
byte* RefId_Buf = NULL;
byte* refid_bufp = NULL;
// ------------ Relation的参考成员中还包括 member_type 和 role -----
byte* Type_Buf = NULL; // member_type (0-node,1-way,2-relation)
byte* type_bufp = NULL;
byte* Role_Buf = NULL; // member_role (string)
byte* role_bufp = NULL;
protected:
// 字符串表操作的基础
// 用STL的map来完成字符串的查找和插入,key和val一同(交替)处理
map<string,uint32_t> string_index;
map<string,uint32_t>::iterator it_map; // 迭代器
uint32_t index = 0; // 字符串索引计数器
// delta 编码的基础
int64_t temp_id;
uint32_t temp_lat,temp_lon;
int64_t temp_cset;
int64_t temp_time;
int64_t temp_ref[3]; // for node, way, relation
// 对象数量计数器
int count = 0; // count< group_size
int osm_type = -1; // 当前处理的OSM类型标记,0-node,1-way,2-relation
public:
OSMBuffer(){};
void osm_reset_vars(){
// 重置所有的delta编码计数器,应该在写完一个文件块之后调用
temp_id = 0;
temp_lon = temp_lat = 0;
temp_cset = 0;
temp_time = 0;
temp_ref[0] = temp_ref[1] = temp_ref[2]=0;
count = 0;
index = 0;
string_index.clear(); //
}
void osm_reset(){ // 缓冲区指针头重置
block_bufp = Block_Buf; // 主缓冲区
id_bufp = Id_Buf; // id
lon_bufp = Lon_Buf; // lon
lat_bufp = Lat_Buf; // lat
keyval_bufp = KeyVal_Buf; // key/val
keyvalid_bufp = KeyValId_Buf; // key/val的索引指针的重置
// valid_bufp = ValId_Buf;
version_bufp = Version_Buf; // version
time_bufp = Time_Buf; // timestamp
cset_bufp = Cset_Buf; //changset
uid_bufp = Uid_Buf; // user_id
uname_bufp = Uname_Buf; // user_name
refid_bufp = RefId_Buf; // refID
type_bufp = Type_Buf; // member_type
role_bufp = Role_Buf; // member_role
}
void osm_end() {
// 清理模块内的缓冲区
// 将在程序结尾调用;
delete [] Block_Buf;
delete [] Id_Buf; // id
delete [] Lon_Buf; // lon
delete [] Lat_Buf; // lat
delete [] KeyVal_Buf; // key/val
delete [] KeyValId_Buf;
// delete [] ValId_Buf;
delete [] Version_Buf; // version
delete [] Time_Buf; // timestamp
delete [] Cset_Buf; //changset
delete [] Uid_Buf; // user_id
delete [] Uname_Buf; // user_name
delete [] RefId_Buf; // refID
delete [] Type_Buf; // member_type
delete [] Role_Buf; // member_role
file.close();
} // end
int osm_ini(const std::string filename){
// 初始化类,须在调用其他函数之前调用它
// 返回值: 0-正常 !0 - 出错
file.open(filename); // 打开输出文件
if(!file.is_open()){
std::cerr<<"Can't open "<<filename<<" file for output\n";
exit(EXIT_FAILURE);
}
Block_Buf = new byte[max_block_size];
// --------------数值型 9个 --------------------
Id_Buf = new byte[max_numberbuf_size];
Lon_Buf = new byte[max_numberbuf_size];
Lat_Buf = new byte[max_numberbuf_size];
Version_Buf = new byte[max_numberbuf_size];
Time_Buf = new byte[max_numberbuf_size];
Cset_Buf = new byte[max_numberbuf_size];
Uid_Buf = new byte[max_numberbuf_size];
RefId_Buf = new byte[max_numberbuf_size];
Type_Buf = new byte[max_numberbuf_size];
KeyValId_Buf = new byte[max_numberbuf_size]; // 索引
// ValId_Buf = new byte[max_numberbuf_size];
// -------------字符串型 4个 --------------------
KeyVal_Buf = new byte[max_stringbuf_size];
Uname_Buf = new byte[max_stringbuf_size];
Role_Buf = new byte[max_stringbuf_size];
osm_reset();
return 0;
}
// 返回各缓冲区的内存占用情况
int Block_Buf_Size(){ return block_bufp - Block_Buf;}
int Id_Buf_Size(){ return id_bufp - Id_Buf; }
int Lon_Buf_Size(){ return lon_bufp - Lon_Buf; }
int Lat_Buf_Size(){ return lat_bufp - Lat_Buf; }
int Version_Buf_Size(){ return version_bufp - Version_Buf; }
int Time_Buf_Size(){ return time_bufp - Time_Buf;}
int Cset_Buf_Size(){ return cset_bufp - Cset_Buf;}
int Uid_Buf_Size(){ return uid_bufp - Uid_Buf;}
int RefId_Buf_Size(){ return refid_bufp - RefId_Buf;}
int Type_Buf_Size(){ return type_bufp - Type_Buf;}
int KeyVal_Buf_Size(){ return keyval_bufp - KeyVal_Buf; }
int KeyValId_Buf_Size(){return keyvalid_bufp - KeyValId_Buf;}
// int ValId_Buf_Size(){return valid_bufp - ValId_Buf;}
int Uname_Buf_Size(){ return uname_bufp - Uname_Buf;}
int Role_Buf_Size(){ return role_bufp - Role_Buf; }
// 判断各缓冲区是否接近满
bool Block_Buf_Full(int total_len){
// 判断主缓冲区是否有足够的剩余空间存储 total_len 长度的数据
return Block_Buf_Size() + total_len >0.9*max_block_size?true:false;
}
// 数值型
bool Id_Buf_Full(){
return Id_Buf_Size()> 0.9*max_numberbuf_size?true:false;
}
bool Lon_Buf_Full(){
return Lon_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool Lat_Buf_Full(){
return Lat_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool Version_Buf_Full(){
return Version_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool Time_Buf_Full(){
return Time_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool Cset_Buf_Full(){
return Cset_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool Uid_Buf_Full(){
return Uid_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool RefId_Buf_Full(){
return RefId_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool Type_Buf_Full(){
return Type_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
bool KeyValId_Buf_Full(){
return KeyValId_Buf_Size()>0.9*max_numberbuf_size?true:false;
}
// bool ValId_Buf_Full(){
// return ValId_Buf_Size()>0.9*max_numberbuf_size?true:false;
// }
// 字符串型
bool KeyVal_Buf_Full(){
return KeyVal_Buf_Size()>0.9*max_stringbuf_size?true:false;
}
bool Uname_Buf_Full(){
return Uname_Buf_Size()>0.9*max_stringbuf_size?true:false;
}
bool Role_Buf_Full(){
return Role_Buf_Size()>0.9*max_stringbuf_size?true:false;
}
// ---- 1. 对数值型缓冲区的 写入操作 ----------------------------
// 功能说明: 提供将 不同类型的数据 以varint 方式写入对应缓冲区的功能
// 注意: *************暂时没有加入缓冲区满空状态检查*************
// ------ 1.1 无重复的数值型数据的写入处理 ----------------------
// 写id
void write_id(int64_t id){
// 向 ID 缓冲区中以varint方式写入id
// 从缓冲区数据当前位置 id_bufp 开始写入
int len = sint64_buf(id_bufp, id); // 缓冲区当前位置指针后移
id_bufp +=len;
}
// 写经纬度坐标 (输入为 int32_t 整数)
void write_lon(int32_t lon){
lon_bufp += sint32_buf(lon_bufp,lon);
}
void write_lat(int32_t lat){
lat_bufp += sint32_buf(lat_bufp,lat);
}
// 写 用户信息 (数值部分)
void write_version(uint32_t version){
version_bufp +=uint32_buf(version_bufp,version);
}
void write_time(int64_t timestamp){
time_bufp +=sint64_buf(time_bufp,timestamp);
}
void write_cset(int64_t changset){
cset_bufp +=sint64_buf(cset_bufp,changset);
}
void write_uid(uint32_t uid){ // 是不是应该把 uid和uname放一起组成字符串
uid_bufp +=uint32_buf(uid_bufp,uid);
}
// ------ 1.2 重复的数值型数据字段 --------------------------------
//////////////////////////////////////////////////////////////////
// ------ 2. key/val 写入 key_val 缓冲区的操作 -------------------
void write_keyvalid(uint32_t stringid){
// 向 keyvalid 缓冲区中写入一个 无符号32位整数
keyvalid_bufp += uint32_buf(keyvalid_bufp,stringid);
}
int write_keyval(const char* s){
// 写一个“以零为终止符”的字符串; 、、、考虑改输入类型为string
// 在 keyval_bufp 位置开始写,写入完成 keyval_bufp指针将后移
// 返回:增加的字节数
byte* p0;
byte c;
p0 = keyval_bufp;
// ---- 检查满空状态 -------
do *keyval_bufp++= c=*s++;
while(c!=0);
return keyval_bufp - p0; // 返回是写入的字节数
}
void write_tags(const Tags &tags){
// 向 key/val缓冲区中写入字符串
// 通过map的查找功能滤掉重复的字符串
// keyid/valid 也存入同一缓冲区,交替存储
// keyval_id 缓冲区的存储模式为:** size + (重复的)key_id + val_id **
// /////并向 keyvalid 缓冲区中写入对应的索引值
// 先向 keyvalid缓冲区中写入 key/val的数量
write_keyvalid(tags.size());
// 处理key/val
for (auto it = tags.begin();it!=tags.end();++it){ // 对tags中的每对 key/val进行遍历
// 得到 key/val
std::string key = it->first;
std::string val = it->second;
// key 的处理
it_map = string_index.find(key); // 在map中寻找key
if(it_map != string_index.end()){
// map中已经存在相同的字符串
// 直接把该字符串的索引存入 keyvalid 缓冲区即可,索引在second中
write_keyvalid(it_map->second);
}
else {
// map中尚不存在该字符串,则 将该字符串存进map, string - index
string_index[key] = index;
// 将该字符串存进 key/val缓冲区
write_keyval(key.c_str()); // 字符串以零做分隔符
// 把这个key的索引值存入 keyvalid缓冲区
write_keyvalid(index++);
}
// val 的处理
it_map = string_index.find(val); // 在map中寻找val
if (it_map != string_index.end()){
write_keyvalid(it_map->second);
}
else {
string_index[val] = index;
write_keyval(val.c_str());
write_keyvalid(index++);
}
}
}
// ------- 写入对象的操作 ----------------------------------------
void write_byte_toblockbuf(byte b) {
// 向 主缓冲区 中写入一个字节(字符);
*block_bufp++ = b;
} // end
void write_len_toblockbuf(uint32_t len){
block_bufp +=uint32_buf(block_bufp,len);
}
int write_Node(int64_t id,double lon,double lat,Info info,Tags tags){
// 将一组(≤10000)个节点写入文件块
// 接口: 经纬度坐标为double型,需乘以放大倍数1e7并转换类型为int32_t
// 返回: 0 成功; !=0 失败
// 将id写入id缓冲区(delta-coding)
if (osm_type == -1){
// 第一次运行
osm_reset_vars(); // delta计数器重置
osm_reset(); // 缓冲区指针重置
}
else if (osm_type !=0){
write_block();
SerializeToFile();
// 1)当前处理的对象类型与之前发生了改变,
// 2)或者写入一组对象数量已满时,
// 需要先将之前的各缓冲区序列化进文件
/*
if (osm_type =1){
// way
//------ 调用序列化way缓冲区的写入文件操作 ---------
}
else if (osm_type =2){
// relation
//------ 调用序列化relation缓冲区的写入文件操作-----
}
osm_reset_vars(); // delta计数器重置
osm_reset(); // 缓冲区指针重置
*/
}
else if (osm_type==0 && count>=group_size){
// 对象数量已满
// --------- 调用序列化node缓冲区的写入文件操作 --------
write_block();
// SerializeToFile(osm_type,&file);
SerializeToFile();
// cout << osm_type;
// osm_reset_vars(); // delta计数器重置
// osm_reset(); // 缓冲区指针重置
}
osm_type = 0; // 设置类型标识为node
count++; // 对象计数器加1
write_id(id-temp_id); temp_id = id;
// 将lon写入lon缓冲区 delta-coding
write_lon(int32_t(lon*1e7)-temp_lon); temp_lon = int32_t(lon*1e7);
// 将lat写入lat缓冲区 delta-coding
write_lat(int32_t(lat*1e7)-temp_lat); temp_lat = int32_t(lat*1e7);
write_version(info.version);
write_time(info.timestamp-temp_time); temp_time = info.timestamp;
write_cset(info.changeset-temp_cset); temp_cset = info.changeset;
write_tags(tags); // 写key/val
}
///////////////////////////////////////////////////////////////////////////////
// ------------------- 将各字段array写入主缓冲区的操作 -------------------
void write_id_array(){
// 将 ID array写入 blockbuf id标识符+长度+id数据
int len = Id_Buf_Size(); // 获取id列的长度
write_byte_toblockbuf(0x11); //写id的标识符 0x11
write_len_toblockbuf(len); // 写入id列的长度信息
memcpy(block_bufp,Id_Buf,len); // 写入id列的数据内容
block_bufp += len; // 主缓冲区指针后移
}
void write_lon_array(){
// 将 节点坐标 的lon array写入 blockbuf lon标识符+长度+数据
int len = Lon_Buf_Size();
write_byte_toblockbuf(0x12); //写lon的标识符 0x12
write_len_toblockbuf(len);
memcpy(block_bufp,Lon_Buf,len); // 写入id列的数据内容
block_bufp +=len;
}
void write_lat_array(){
// 将 节点坐标 的lat array写入 blockbuf lat标识符+长度+数据
int len = Lat_Buf_Size();
write_byte_toblockbuf(0x13); //写lat的标识符 0x13
write_len_toblockbuf(len);
memcpy(block_bufp,Lat_Buf,len);
block_bufp +=len;
}
void write_version_array(){
// 写用户信息 版本号
int len = Version_Buf_Size();
write_byte_toblockbuf(0x21); // version 的标识符 0x21
write_len_toblockbuf(len);
memcpy(block_bufp,Version_Buf,len);
block_bufp += len;
}
void write_time_array(){
// 写用户信息 时间戳
int len = Time_Buf_Size();
write_byte_toblockbuf(0x22); // timestamp 的标识符 0x22
write_len_toblockbuf(len);
memcpy(block_bufp,Time_Buf,len);
block_bufp += len;
}
void write_cset_array(){
// 写用户信息 变更集
int len = Cset_Buf_Size();
write_byte_toblockbuf(0x23); // changeset 的标识符 0x23
write_len_toblockbuf(len);
memcpy(block_bufp,Cset_Buf,len);
block_bufp += len;
}
void write_uid_array(){ // 是不是应该把 uid和uname放一起组成字符串
// 写用户信息 用户id
int len = Uid_Buf_Size();
write_byte_toblockbuf(0x24); // user_id 的标识符 0x24
write_len_toblockbuf(len);
memcpy(block_bufp,Uid_Buf,len);
block_bufp += len;
}
void write_keyvalid_array(){
// 1) 先把keyval的索引写入 block (即 将keyvalid_buf 拷贝进 block_buf)
int len = KeyValId_Buf_Size();
write_byte_toblockbuf(0x31); // keyvalid 的标识符
write_len_toblockbuf(len);
memcpy(block_bufp,KeyValId_Buf,len);
block_bufp +=len;
}
void write_keyval_array(){
// 写 key/val 字符串对array 到主缓冲区
int len = KeyVal_Buf_Size(); // 获取key/val 缓冲区的大小
write_byte_toblockbuf(0x41); // key/val 字符串表的标识符
write_len_toblockbuf(len);
memcpy(block_bufp,KeyVal_Buf,len);
block_bufp += len;
}
// ------------------- 将各字段array写入主缓冲区的操作 结束 ---------------
////////////////////////////////////////////////////////////////////////////////
int write_block(){
// 将列式存储的一组OSM对象的缓冲区写入主缓冲区
// osm_type: 0-node,1-way,2-relation
int total_len = 0; // 记录块中数据总长度
int len = 0; // 记录各缓冲区长度
if (osm_type == 0){
// node
total_len = Id_Buf_Size() + Lon_Buf_Size() + Lat_Buf_Size()+
KeyVal_Buf_Size() + KeyValId_Buf_Size() + Version_Buf_Size()
+ Time_Buf_Size() + Cset_Buf_Size();
if (Block_Buf_Full(total_len)){ // 主缓冲区剩余空间不足
std::cerr<<"Error: block-buffer overflow!\n";
exit(EXIT_FAILURE);
}
write_keyval_array(); // 在 block 头首先写入 keyval 字符串array缓冲区
write_id_array();
write_lon_array();
write_lat_array();
write_keyvalid_array(); //写入 字符串索引缓冲区 到block
write_version_array();
write_time_array();
write_cset_array();
}
else if (osm_type = 1){
// way
}
else if (osm_type = 2){
// relation
}
}
////////////// 测试用 ////////////////////////////////////
int memcpy_ID_toblock(){
int len = Id_Buf_Size(); // 获取id列的长度
write_byte_toblockbuf(0x11); //写id的标识符 0x11
write_len_toblockbuf(len); // 写入id列的长度信息
memcpy(block_bufp,Id_Buf,len); // 写入id列的数据内容
block_bufp += len; // 主缓冲区指针后移
}
void show_id_buffer(){
int i;
for (i=0;i<Id_Buf_Size();++i)
cout<<"0x"<<std::hex<<int(Id_Buf[i])<<" ";
cout<<endl;
}
void show_block_buffer(){
int i;
for (i=0;i<Block_Buf_Size();++i)
cout<<"0x"<<std::hex<<int(Block_Buf[i])<<" ";
cout<<endl;
}
// int SerializeToFile(){
// }
///////////// 测试用 ////////////////////////////////////
// int SerializeToFile(int type,std::ofstream &file){
int SerializeToFile(){
// 将block内容写入文件
// 对象类型 - 长度 - 数据
byte lenbuf[30];
int block_len = Block_Buf_Size();
// 写入block对象类型
file.put(osm_type+0x01); // 0x01 - node, 0x02 - way, 0x03-relation
// 写入block长度
file.write((char*)&lenbuf,uint32_buf(lenbuf,block_len));
// 写入block数据
file.write((char*) Block_Buf,block_len);
osm_reset(); // 指针重置
osm_reset_vars(); // delta编码计数器重置
}
void write_end_tofile(){
file.put(0xfe);
}
};
网友评论