Skip to content

Instantly share code, notes, and snippets.

@UplinkCoder
Last active January 23, 2022 22:40
Show Gist options
  • Select an option

  • Save UplinkCoder/3b8704a93d20367edce5fd472a758dd6 to your computer and use it in GitHub Desktop.

Select an option

Save UplinkCoder/3b8704a93d20367edce5fd472a758dd6 to your computer and use it in GitHub Desktop.
/*
Copyright (c) 2012, Canal TP
This is an example file, do whatever you want with it! (for example if you are in Paris, invite us for a beer)
A slightly more advanced example : we want to use OSM data to extract a graph that represents the road network
Build the example with (we're to lazy to make it compatible with older standards):
g++ example_routing.cc -O2 -losmpbf -lprotobuf -std=c++0x -o routing
To run it:
./routing path_to_your_data.osm.pbf
*/
#include <unordered_map>
#include <map>
#include <set>
#include "osmpbfreader.h"
#include <iostream>
#include <string>
#include <stdlib.h>
#include "crc32.c"
#include "string_view.hpp"
#define cast(T) (T)
using namespace CanalTP;
using namespace std;
using short_tags_t = unordered_map<uint32_t, uint32_t>;
using namespace bpstd;
struct Offsets
{
const uint32_t NameTable = 0;
const uint32_t ValueTable = 4;
// const uint32_t
} ;
struct Serializer
{
enum class serialize_mode_t { Reading, Writing };
FILE* fd;
const char* m_filename;
uint64_t bytes_in_file = 0;
uint64_t position_in_file = 0;
uint32_t position_in_buffer = 0;
uint32_t buffer_used = 0;
#define BUFFER_SIZE 1024
uint8_t buffer[BUFFER_SIZE];
private:
void RefillBuffer(void)
{
uint64_t bytes_avilable = bytes_in_file - position_in_file > 0;
const auto old_bytes_in_buffer = buffer_used - position_in_buffer;
assert(bytes_avilable > 0);
uint32_t size_to_read = BUFFER_SIZE - buffer_used;
if (bytes_avilable < size_to_read)
size_to_read = bytes_avilable;
memmove(buffer, buffer + position_in_buffer, old_bytes_in_buffer);
auto bytes_read = fread(buffer + old_bytes_in_buffer, 1, size_to_read, fd);
assert(bytes_read == size_to_read);
position_in_file += size_to_read;
}
public:
uint32_t ReadShortInt(void) {
assert(position_in_buffer < buffer_used);
if (position_in_buffer > (1024 - 4) && bytes_in_file - position_in_file > 0)
{
RefillBuffer();
}
assert(buffer_used > 1);
auto mem = buffer + position_in_buffer;
const auto first_byte = *mem;
uint32_t result = first_byte & ~((1 << 6) - 1);
const auto bytes_used = first_byte >> 6;
switch(bytes_used)
{
case 3:
result |= (*(mem + 3)) << 22;
[[fallthrough]];
case 2:
result |= (*(mem + 2)) << 14;
[[fallthrough]];
case 1:
result |= (*(mem + 2)) << 6;
[[fallthrough]];
case 0: break; // we already did this
}
mem += bytes_used;
buffer_used -= bytes_used;
}
void WriteFlush()
{
uint32_t bytes_to_flush = 512;
if (position_in_buffer < bytes_to_flush)
bytes_to_flush = position_in_buffer;
fwrite(buffer, 1, bytes_to_flush, fd);
position_in_buffer -= bytes_to_flush;
// cpy overhang
memmove(buffer, buffer + bytes_to_flush, position_in_buffer);
}
Serializer(const char* filename, serialize_mode_t mode) :
m_filename(filename) {
assert(mode == serialize_mode_t::Writing); // for now only writing is supported
fd = fopen(filename, "wb");
if (!fd)
{
perror("Serializer()");
}
else
{
if (mode == serialize_mode_t::Writing)
{
fwrite("OSMb", 4, 1, fd); // write magic number
uint32_t versionNumber = 1;
if (ferror(fd))
{
perror("Serializer()");
}
fwrite(&versionNumber, 4, 1, fd); // write magic number
}
}
}
~Serializer()
{
// TODO maybe pad the file to a multiple of 4?
WriteFlush();
fclose(fd);
}
// Returns number of bytes written. 0 means error.
uint8_t WriteShortInt(uint32_t value) {
assert(value < 0x40000000);
if (position_in_buffer >= 512)
{
// try to flush in 512 chunks
WriteFlush();
}
if (value < (1 << 6))
{
buffer[position_in_buffer++] = value;
}
else if (value < (1 << 14))
{
buffer[position_in_buffer++] = (value & ((1 << 6) - 1)) | 1 << 6;
buffer[position_in_buffer++] = (value >> 6);
}
else if (value < 1 << 22)
{
buffer[position_in_buffer++] = (value & ((1 << 6) - 1)) | 2 << 6;
buffer[position_in_buffer++] = (value >> 6) & 0xff;
buffer[position_in_buffer++] = (value >> 14) & 0xff;
}
else
{
buffer[position_in_buffer++] = (value & ((1 << 6) - 1)) | 2 << 6;
buffer[position_in_buffer++] = (value >> 6) & 0xff;
buffer[position_in_buffer++] = (value >> 14) & 0xff;
buffer[position_in_buffer++] = (value >> 22) & 0xff;
}
}
uint32_t ReadU32(void) {
if (position_in_buffer < buffer_used - 4)
{
RefillBuffer();
}
assert(buffer_used - position_in_buffer <= 4);
uint32_t result = (*(uint32_t*)(buffer + position_in_buffer));
position_in_buffer += 4;
return result;
}
void WriteU32(uint32_t value) {
if (position_in_buffer >= 512)
{
// try to flush in 512 chunks
WriteFlush();
}
(*(uint32_t*)(buffer + position_in_buffer)) = value;
position_in_buffer += 4;
}
#undef BUFFER_SIZE
} ;
template <typename map_t>
bool CanFind(const map_t& map, const typename map_t::value_type && key_value) {
bool result = false;
for(auto it = map.find(key_value.first);
it != map.end();
it++)
{
if (it->second == key_value.second)
{
result = true;
break;
}
}
return result;
}
// We keep every node and the how many times it is used in order to detect crossings
struct Node {
Node() : osmid(0), uses(0), lon_m(0), lat_m(0), tags({}) {}
Node(uint64_t osmid_, double lon, double lat, short_tags_t tags_) :
osmid(osmid_), uses(0), lon_m(lon), lat_m(lat), tags(tags_) {}
uint64_t osmid;
int32_t uses;
double lon_m;
double lat_m;
//Tags tags;
short_tags_t tags;
};
struct Way
{
Way(uint64_t osmid_, std::vector<uint64_t> refs_, short_tags_t tags_) :
osmid(osmid_), refs(refs_), tags(tags_) {}
uint64_t osmid;
std::vector<uint64_t> refs;
short_tags_t tags;
};
string_view findName(const Tags tags)
{
string_view name = {};
auto entry = tags.find("name");
if (entry != tags.end()) {
name = entry->second;
}
return name;
}
struct StringEntry
{
uint32_t crc32;
uint32_t length;
uint32_t offset;
};
#define SORT_VEC(VEC) \
VEC.data(), VEC.size(), sizeof(decltype(VEC)::value_type)
struct StringTable
{
StringTable() : string_data(), strings(), crc32_to_indecies() {}
// private:
std::vector<char> string_data;
std::vector<StringEntry> strings;
map<uint32_t, uint32_t> crc32_to_indecies;
std::vector<std::pair<uint32_t, uint32_t> > usage_counts;
// public :
void assert_sorted(void)
{
uint32_t lastCrc = 0;
for(auto s : strings)
{
uint32_t s_crc32 = s.crc32;
assert(s_crc32 >= lastCrc);
lastCrc = s.crc32;
}
return ;
}
auto AddString(const string_view & str) {
// cerr << "called " << __FUNCTION__ << " (" << str << ")" << endl;
const auto crc = calc_table(str.data(), str.size());
uint32_t idx = 0;
for (auto it = crc32_to_indecies.find(crc);
it != crc32_to_indecies.end();
it++)
{
const auto entry = strings[it->second - 1];
const auto canidate = string_view {&string_data[entry.offset], entry.length};
if (canidate == str)
{
idx = it->second;
usage_counts[idx - 1].second++;
break;
}
}
if (!idx)
// counldn't find the string insert it
{
uint32_t offset = (uint32_t) string_data.size();
StringEntry entry { crc, (uint32_t)str.size(), offset };
string_data.insert(string_data.end(), str.begin(), str.end());
string_data.push_back('\0');
strings.push_back(entry);
idx = strings.size();
usage_counts.push_back({idx, 1});
crc32_to_indecies.emplace(crc, idx);
}
return idx;
}
void operator+= (const string_view & str) {
(void)AddString(str);
}
/// Returns 0 if not found or the index of the string_entry + 1
uint32_t LookupCString(const char* str) {
return LookupString(string_view {str, strlen(str)});
}
uint32_t LookupString(const string_view& str) {
const char* str_data = str.data();
const auto str_size = str.size();
const uint32_t crc_input = calc_table(str.data(), str.size());
uint idx = 0;
// using entry_t = __decltype(strings)::value_type;
for (auto it = crc32_to_indecies.find(crc_input);
it != crc32_to_indecies.end();
it++
) {
const auto s = strings[it->second - 1];
if (str_size == s.length
&& 0 == strncmp(&string_data[s.offset], str_data, str_size))
{
idx = (it->second);
break;
}
}
return idx;
}
string_view LookupId(uint32_t idx) {
if (!idx || idx > strings.size())
{
return {(const char*)0, 0};
}
else
{
const auto entry = strings[idx - 1];
return string_view {&string_data[entry.offset], entry.length};
}
}
string_view operator[] (uint32_t idx) {
return LookupId(idx);
}
uint32_t operator[] (const string_view& str) {
return LookupString(str);
}
vector<uint8_t> serialize () {
return {};
}
};
#undef SORT_VEC
struct SerializeWays
{
std::unordered_map<uint64_t, Node> nodes;
set<uint32_t> street_names_indicies {};
StringTable tag_names {};
StringTable tag_values {};
vector<Way> ways;
short_tags_t ShortenTags(const Tags& tags) {
short_tags_t result = {};
for(auto it = tags.begin();
it != tags.end();
it++)
{
auto name_id = tag_names.AddString(it->first);
auto value_id = tag_values.AddString(it->second);
result.emplace(name_id, value_id);
}
return result;
}
void node_callback(uint64_t osmid, double lon, double lat, const Tags &tags) {
this->nodes[osmid] = Node(osmid, lon, lat, ShortenTags(tags));
}
// This method is called every time a Way is read
void way_callback(uint64_t osmid, const Tags &tags, const std::vector<uint64_t> &refs){
// If the way is part of the road network we keep it
// There are other tags that correspond to the street network, however for simplicity, we don't manage them
// Homework: read more properties like oneways, bicycle lanes…
if(tags.find("highway") != tags.end()) {
const auto& name = findName(tags);
uint32_t name_index = 0;
if (name.size())
{
street_names_indicies.emplace(name_index);
}
}
ways.push_back({osmid, refs, ShortenTags(tags)});
}
// We don't care about relations
void relation_callback(uint64_t /*osmid*/, const Tags &/*tags*/, const References & /*refs*/){}
};
struct Routing {
// Map that stores all the nodes read
std::unordered_map<uint64_t, Node> nodes;
// Stores all the nodes of all the ways that are part of the road network
// ulong[][] ways;
std::vector<Way> ways;
// This method is called every time a Node is read
void node_callback(uint64_t osmid, double lon, double lat, const Tags &tags) {
this->nodes[osmid] = Node(osmid, lon, lat, {});
}
// This method is called every time a Way is read
void way_callback(uint64_t osmid, const Tags &tags, const std::vector<uint64_t> &refs){
// If the way is part of the road network we keep it
// There are other tags that correspond to the street network, however for simplicity, we don't manage them
// Homework: read more properties like oneways, bicycle lanes…
if(tags.find("highway") != tags.end()) {
ways.push_back({osmid, refs, {}});
}
}
// Once all the ways and nodes are read, we count how many times a node is used to detect intersections
void count_nodes_uses() {
for(auto way : ways){
auto refs = way.refs;
for(uint64_t ref : refs) {
nodes.at(ref).uses++;
}
// make sure that the last node is considered as an extremity
nodes.at(refs.back()).uses++;
}
}
// Returns the source and target node of the edges
std::vector< std::pair<uint64_t, uint64_t> > edges() {
std::vector< std::pair<uint64_t, uint64_t> > result;
for(auto way : ways) {
auto refs = way.refs;
if(refs.size() > 0) {
uint64_t source = refs[0];
for(size_t i = 1; i < refs.size(); ++i) {
uint64_t current_ref = refs[i];
// If a node is used more than once, it is an intersection, hence it's a node of the road network graph
if(nodes.at(current_ref).uses > 1) {
// Homework: measure the length of the edge
uint64_t target = current_ref;
auto src_node = nodes[source];
auto tgt_node = nodes[target];
result.push_back(std::make_pair(source, target));
source = target;
}
}
}
}
return result;
}
// We don't care about relations
void relation_callback(uint64_t /*osmid*/, const Tags &/*tags*/, const References & /*refs*/){}
};
int main(int argc, char** argv) {
if(argc != 2 && argc != 3) {
std::cout << "Usage: " << argv[0] << " file_to_read.osm.pbf" << std::endl;
return 1;
}
// Let's read that file !
// Routing routing;
// read_osm_pbf(argv[1], routing);
// std::cout << "We read " << routing.nodes.size() << " nodes and " << routing.ways.size() << " ways" << std::endl;
// routing.count_nodes_uses();
// std::cout << "The routing graph has " << routing.edges().size() << " edges" << std::endl;
SerializeWays serializeWays;
read_osm_pbf(argv[1], serializeWays);
uint32_t maxTags = 0;
/*
for (auto& nt : serializeWays.nodes)
{
auto &node = nt.second;
if (node.tags.size() > maxTags)
maxTags = node.tags.size();
for(auto it = node.tags.begin();
it != node.tags.end();
it++)
{
printf(" %s : %s\n", serializeWays.tag_names[it->first].data(),
serializeWays.tag_values[it->second].data());
}
printf("\n");
}
*/
printf("max number of tags: %d\n", maxTags);
printf("max number of tag-keys: %d\n", (int)serializeWays.tag_names.strings.size());
printf("max number of tag-values: %d\n", (int)serializeWays.tag_values.strings.size());
/*
* amenity : fast_food
name : Asia-Imbiss
name:de : Asia-Imbiss
opening_hours : Mo-Fr 11:00-20:00; Sa 11:00-19:00
wheelchair : yes
*/
#define PV(NAME) \
// cout << #NAME << ": " << NAME << endl;
auto amenity_nt_id = serializeWays.tag_names["amenity"];
PV(amenity_nt_id);
auto name_nt_id = serializeWays.tag_names["name"];
PV(name_nt_id);
auto opening_hours_nt_id = serializeWays.tag_names["opening_hours"];
PV(opening_hours_nt_id);
auto asia_imbiss_vt_id = serializeWays.tag_values["Asia-Imbiss"];
PV(asia_imbiss_vt_id);
auto opening_times_vt_id = serializeWays.tag_values["Mo-Fr 11:00-20:00; Sa 11:00-19:00"];
PV(opening_times_vt_id);
auto fast_food_vt_id = serializeWays.tag_values["fast_food"];
PV(fast_food_vt_id);
#undef PV
for(auto & nt : serializeWays.nodes)
{
auto &node = nt.second;
if (
CanFind(node.tags, {name_nt_id, asia_imbiss_vt_id})
&& CanFind(node.tags, {opening_hours_nt_id, opening_times_vt_id})
&& CanFind(node.tags, {amenity_nt_id, fast_food_vt_id})
)
{
printf("found the thingy thing ref: %llu\n", node.osmid);
}
else
{
// printf("No found no thingy\n");
}
}
Serializer s {"test.dat", Serializer::serialize_mode_t::Writing};
// u32 -- number of tags names
// u32 -- string data length for tag names
s.WriteU32(serializeWays.tag_names.strings.size());
s.WriteU32(serializeWays.tag_names.string_data.size());
return 0;
}
/*
* building | yes • house • residential • garage
source | BAG • Bing • cadastre-dgi-fr␣source␣:␣Direction␣Générale␣des␣Impôts␣-␣Cadastre.␣Mise␣à␣jour␣:␣2010 • cadastre-dgi-fr␣source␣:␣Direction␣Générale␣des␣Impôts␣-␣Cadastre.␣Mise␣à␣jour␣:␣2011 • cadastre-dgi-fr␣source␣:␣Direction␣Générale␣des␣Impôts␣-␣Cadastre.␣Mise␣à␣jour␣:␣2012 • bing • NRCan-CanVec-10.0 • microsoft/BuildingFootprints • digitalglobe • YahooJapan/ALPSMAP
highway | residential • service • track • footway • unclassified • path • tertiary • crossing • secondary • primary
addr:housenumber | 1 • 2 • 3 • 4 • 5 • 6 • 7 • 8 • 10 • 9
addr:street |
addr:city |
name |
addr:postcode |
natural | tree • water • wood • scrub • wetland • grassland • coastline • tree_row • peak • bare_rock
surface | asphalt • unpaved • paved • ground • concrete • paving_stones • gravel • dirt • grass • compacted
landuse | residential • farmland • grass • forest • meadow • orchard • farmyard • industrial • vineyard • cemetery
addr:country | DE • DK • EG • AT • US • CZ • RU • SK • IT • EE
source:date | 2014-03-24 • 2014-02-11 • 2014-05-07 • 2013-11-26 • 2018-12-14 • 201011 • 2014-05-20 • 1989-07-01
power | tower • pole • generator • line • minor_line • substation
waterway | stream • ditch • river • drain • canal
building:levels | 1 • 2 • 3 • 4 • 5 • 6
amenity | parking • bench • place_of_worship • restaurant • school • parking_space • waste_basket • fuel • cafe • fast_food
service | driveway • parking_aisle • alley • yard • spur • siding
oneway | yes • no
barrier | fence • gate • wall • hedge • kerb • bollard • lift_gate • retaining_wall
access | private • yes • customers • no • permissive • destination
height | 3 • 6 • 5 • 4 • 2 • 7 • 10
start_date | 1970 • 1950 • 1972 • 1975 • 1960 • 1980 • 1973 • 1965 • 1974 • 1968
ref
addr:state | NY • FL • CT • CA • MD • ME • KY • AZ • NC • CO
maxspeed | 50 • 30 • 40 • 60 • 80 • 30␣mph • 70 • 100 • 25␣mph • 20
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment