/* * Navit, a modular navigation system. * Copyright (C) 2005-2011 Navit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "maptool.h" #include "debug.h" #include "linguistics.h" #include "file.h" #include "fileformat.pb-c.h" #include "osmformat.pb-c.h" static double latlon_scale=10000000.0; struct db_config { int node_ids_per_file; int node_ids_per_blob; int node_ids_per_group; int way_ids_per_file; int way_ids_per_blob; int way_ids_per_group; int relation_ids_per_file; int relation_ids_per_blob; int relation_ids_per_group; } db_config = { 200000, 30000, 500, 40000, 1000, 30, 10000, 500, 20, }; struct osm_protobufdb_context { int current_file, current_block, active_block; int in_node, in_way, in_relation; OSMPBF__Node n; OSMPBF__Way w; OSMPBF__Relation r; OSMPBF__Info i; FILE *f; OSMPBF__PrimitiveBlock *pb; OSMPBF__PrimitiveGroup *pg; GHashTable *string_hash; OSMPBF__DenseInfo *di; OSMPBF__DenseNodes *dn; OSMPBF__StringTable *st; } context; static int osm_protobufdb_write_blob(OSMPBF__Blob *blob, FILE *out) { unsigned char lenb[4]; int len,blen; unsigned char *buffer; OSMPBF__BlobHeader header=OSMPBF__BLOB_HEADER__INIT; blen=osmpbf__blob__get_packed_size(blob); header.type="OSMData"; header.datasize=blen; len=osmpbf__blob_header__get_packed_size(&header); buffer=alloca(len); lenb[0]=len>>24; lenb[1]=len>>16; lenb[2]=len>>8; lenb[3]=len; osmpbf__blob_header__pack(&header, buffer); if (fwrite(lenb, sizeof(lenb), 1, out) != 1) return 0; if (fwrite(buffer, len, 1, out) != 1) return 0; buffer=alloca(blen); osmpbf__blob__pack(blob, buffer); if (fwrite(buffer, blen, 1, out) != 1) return 0; return 1; } #if 0 void dump_block(OSMPBF__PrimitiveBlock *pb) { int i,j; printf("%d groups\n",pb->n_primitivegroup); for (i = 0 ; i < pb->n_primitivegroup ; i++) { printf("%d relations\n",pb->primitivegroup[i]->n_relations); for (j = 0 ; j < pb->primitivegroup[i]->n_relations ; j++) { printf("Info %d\n",pb->primitivegroup[i]->relations[j]->info->version); } } } #endif static int osm_protobufdb_finish_block(struct osm_protobufdb_context *ctx) { OSMPBF__Blob *blob,empty_blob=OSMPBF__BLOB__INIT; int len; if (!ctx->pb) return 0; len=osmpbf__primitive_block__get_packed_size(ctx->pb); while (ctx->current_block < ctx->active_block) { osm_protobufdb_write_blob(&empty_blob, ctx->f); ctx->current_block++; } blob=g_malloc(sizeof(*blob)); *blob=empty_blob; blob->has_raw=1; blob->has_raw_size=1; blob->raw.data=g_malloc(len); osmpbf__primitive_block__pack(ctx->pb, blob->raw.data); blob->raw.len=len; blob->raw_size=len; osm_protobufdb_write_blob(blob, ctx->f); osmpbf__blob__free_unpacked(blob, NULL); osmpbf__primitive_block__free_unpacked(ctx->pb, NULL); ctx->pb=NULL; ctx->current_block++; return 1; } static int osm_protobufdb_start_block(struct osm_protobufdb_context *ctx, int blocknum) { OSMPBF__PrimitiveBlock pb=OSMPBF__PRIMITIVE_BLOCK__INIT; OSMPBF__StringTable st=OSMPBF__STRING_TABLE__INIT; if (ctx->active_block == blocknum) return 0; osm_protobufdb_finish_block(ctx); ctx->active_block=blocknum; ctx->pb=g_malloc(sizeof(*ctx->pb)); *ctx->pb=pb; ctx->pb->stringtable=g_malloc(sizeof(*ctx->pb->stringtable)); *ctx->pb->stringtable=st; ctx->st=ctx->pb->stringtable; return 1; } static int osm_protobufdb_start_group(struct osm_protobufdb_context *ctx, int groupnum) { OSMPBF__PrimitiveGroup pg=OSMPBF__PRIMITIVE_GROUP__INIT; if (ctx->pb->n_primitivegroup <= groupnum) { ctx->pb->primitivegroup=g_realloc(ctx->pb->primitivegroup, (groupnum+1)*sizeof(ctx->pb->primitivegroup[0])); while (ctx->pb->n_primitivegroup <= groupnum) { ctx->pb->primitivegroup[ctx->pb->n_primitivegroup]=g_malloc(sizeof(*context.pg)); *ctx->pb->primitivegroup[ctx->pb->n_primitivegroup++]=pg; } g_hash_table_destroy(ctx->string_hash); ctx->string_hash=g_hash_table_new(g_str_hash, g_str_equal); } ctx->pg=ctx->pb->primitivegroup[groupnum]; if (!ctx->pg) { ctx->pg=g_malloc(sizeof(*context.pg)); *ctx->pg=pg; ctx->pb->primitivegroup[groupnum]=ctx->pg; } return 1; } #if 0 static int osm_protobufdb_start_densenode(struct osm_protobufdb_context *ctx) { OSMPBF__DenseInfo di=OSMPBF__DENSE_INFO__INIT; OSMPBF__DenseNodes dn=OSMPBF__DENSE_NODES__INIT; if (!ctx->pg->dense) { ctx->dn=g_malloc(sizeof(*context.dn)); *ctx->dn=dn; ctx->pg->dense=ctx->dn; } else ctx->dn=ctx->pg->dense; if (!ctx->dn->denseinfo) { ctx->di=g_malloc(sizeof(*context.di)); *ctx->di=di; ctx->dn->denseinfo=ctx->di; } else ctx->di=ctx->dn->denseinfo; return 1; } static void osm_protobufdb_write_primitive_group(OSMPBF__PrimitiveGroup *pg, OSMPBF__PrimitiveBlock *pb) { pb->primitivegroup=g_realloc(pb->primitivegroup,(pb->n_primitivegroup+1)*sizeof(OSMPBF__PrimitiveGroup *)); pb->primitivegroup[pb->n_primitivegroup++]=pg; } #endif #define insert(struct, member, pos) {\ int n=struct->n_##member; \ int s=sizeof(struct->member[0]); \ struct->member=g_realloc(struct->member, (n+1)*s); \ memmove(&struct->member[n+1], &struct->member[n], (pos-n)*s); \ memset(&struct->member[n], 0, s); \ struct->n_##member++;\ } #if 0 static int osm_protobufdb_insert_densenode(long long id, OSMPBF__Node *offset, OSMPBF__Info *offseti, OSMPBF__DenseNodes *dn) { int i,l,p; memset(offset, 0, sizeof(*offset)); offseti->timestamp=0; offseti->changeset=0; offseti->user_sid=0; offseti->uid=0; l=dn->n_id; for (i = 0 ; i < l ; i++) { offset->id+=dn->id[i]; offset->lat+=dn->lat[i]; offset->lon+=dn->lon[i]; offseti->timestamp+=dn->denseinfo->timestamp[i]; offseti->changeset+=dn->denseinfo->changeset[i]; offseti->user_sid+=dn->denseinfo->user_sid[i]; offseti->uid+=dn->denseinfo->uid[i]; } p=l; insert(dn, id, p); insert(dn, lat, p); insert(dn, lon, p); insert(dn->denseinfo, version, p); insert(dn->denseinfo, timestamp, p); insert(dn->denseinfo, changeset, p); insert(dn->denseinfo, user_sid, p); insert(dn->denseinfo, uid, p); return p; } static void osm_protobufdb_modify_densenode(OSMPBF__Node *node, OSMPBF__Info *info, OSMPBF__Node *offset, OSMPBF__Info *offseti, int pos, OSMPBF__DenseNodes *dn) { int i; if (pos+1 < dn->n_id) { dn->id[pos+1]+=dn->id[pos]-node->id; dn->lat[pos+1]+=dn->lat[pos]-node->lat; dn->lon[pos+1]+=dn->lon[pos]-node->lon; dn->denseinfo->timestamp[pos+1]+=dn->denseinfo->timestamp[pos]-info->timestamp; dn->denseinfo->changeset[pos+1]+=dn->denseinfo->changeset[pos]-info->changeset; dn->denseinfo->user_sid[pos+1]+=dn->denseinfo->user_sid[pos]-info->user_sid; dn->denseinfo->uid[pos+1]+=dn->denseinfo->uid[pos]-info->uid; } dn->id[pos]=node->id-offset->id; dn->lat[pos]=node->lat-offset->lat; dn->lon[pos]=node->lon-offset->lon; dn->keys_vals=g_realloc(dn->keys_vals, (dn->n_keys_vals+node->n_keys+node->n_vals+1)*sizeof(dn->keys_vals[0])); for (i = 0 ; i < node->n_keys ; i++) { dn->keys_vals[dn->n_keys_vals++]=node->keys[i]; dn->keys_vals[dn->n_keys_vals++]=node->vals[i]; } dn->keys_vals[dn->n_keys_vals++]=0; dn->denseinfo->version[pos]=info->version; dn->denseinfo->timestamp[pos]=info->timestamp-offseti->timestamp; dn->denseinfo->changeset[pos]=info->changeset-offseti->changeset; dn->denseinfo->user_sid[pos]=info->user_sid-offseti->user_sid; dn->denseinfo->uid[pos]=info->uid-offseti->uid; } #endif static int osm_protobufdb_insert_node(long long id, OSMPBF__PrimitiveGroup *pg) { int l,p; OSMPBF__Node node=OSMPBF__NODE__INIT; l=pg->n_nodes; p=l; insert(pg, nodes, p); pg->nodes[p]=g_malloc(sizeof(*pg->nodes[0])); *pg->nodes[p]=node; return p; } static void osm_protobufdb_modify_node(OSMPBF__Node *node, OSMPBF__Info *info, int pos, OSMPBF__PrimitiveGroup *pg) { OSMPBF__Node *n=pg->nodes[pos]; OSMPBF__Info *old_info; g_free(n->keys); g_free(n->vals); old_info=n->info; *n=*node; if (!info) { if (old_info) osmpbf__info__free_unpacked(old_info, NULL); n->info=NULL; } else { if (old_info) n->info=old_info; else n->info=g_malloc(sizeof(*info)); *n->info=*info; } } static int osm_protobufdb_insert_way(long long id, OSMPBF__PrimitiveGroup *pg) { int l,p; OSMPBF__Way way=OSMPBF__WAY__INIT; l=pg->n_ways; p=l; insert(pg, ways, p); pg->ways[p]=g_malloc(sizeof(*pg->ways[0])); *pg->ways[p]=way; return p; } static void osm_protobufdb_modify_way(OSMPBF__Way *way, OSMPBF__Info *info, int pos, OSMPBF__PrimitiveGroup *pg) { OSMPBF__Way *w=pg->ways[pos]; OSMPBF__Info *old_info; int i; long long ref=0; g_free(w->keys); g_free(w->vals); g_free(w->refs); old_info=w->info; *w=*way; for (i = 0 ; i < w->n_refs ; i++) { w->refs[i]-=ref; ref+=w->refs[i]; } if (!info) { if (old_info) osmpbf__info__free_unpacked(old_info, NULL); w->info=NULL; } else { if (old_info) w->info=old_info; else w->info=g_malloc(sizeof(*info)); *w->info=*info; } } static int osm_protobufdb_insert_relation(long long id, OSMPBF__PrimitiveGroup *pg) { int l,p; OSMPBF__Relation relation=OSMPBF__RELATION__INIT; l=pg->n_relations; p=l; insert(pg, relations, p); pg->relations[p]=g_malloc(sizeof(*pg->relations[0])); *pg->relations[p]=relation; return p; } static void osm_protobufdb_modify_relation(OSMPBF__Relation *relation, OSMPBF__Info *info, int pos, OSMPBF__PrimitiveGroup *pg) { OSMPBF__Relation *r=pg->relations[pos]; OSMPBF__Info *old_info; int i; long long ref=0; g_free(r->keys); g_free(r->vals); g_free(r->roles_sid); g_free(r->memids); g_free(r->types); old_info=r->info; *r=*relation; for (i = 0 ; i < r->n_memids ; i++) { r->memids[i]-=ref; ref+=r->memids[i]; } if (!info) { if (old_info) osmpbf__info__free_unpacked(old_info, NULL); r->info=NULL; } else { if (old_info) r->info=old_info; else r->info=g_malloc(sizeof(*info)); *r->info=*info; } } static int osm_protobufdb_string(struct osm_protobufdb_context *ctx, char *str) { char *strd; OSMPBF__StringTable *st=ctx->st; gpointer value; assert(ctx->string_hash != NULL); if (g_hash_table_lookup_extended(ctx->string_hash, str, NULL, &value)) { return (long)value; } if (!st->n_s) { st->n_s++; } strd=g_strdup(str); st->s=g_realloc(st->s, sizeof(st->s[0])*(st->n_s+1)); if (st->n_s == 1) { st->s[0].data=NULL; st->s[0].len=0; } st->s[st->n_s].data=(unsigned char *)strd; st->s[st->n_s].len=strlen(strd); g_hash_table_insert(ctx->string_hash, strd, (gpointer)st->n_s); return st->n_s++; } static int osm_protobufdb_finish_file(struct osm_protobufdb_context *ctx) { osm_protobufdb_finish_block(ctx); if (ctx->f) { fclose(ctx->f); ctx->f=NULL; } ctx->current_file=-1; return 1; } static int osm_protobufdb_start_file(struct osm_protobufdb_context *ctx, int type, int num) { char name[1024]; if (ctx->current_file == num) return 0; osm_protobufdb_finish_file(ctx); sprintf(name,"tst/%d-%08d",type,num); ctx->f=fopen(name,"w"); ctx->current_file=num; ctx->current_block=0; ctx->active_block=-1; return 1; } static void test(void) { context.current_file=-1; } static void finish(void) { osm_protobufdb_finish_file(&context); } static long long osm_protobufdb_timestamp(char *str) { struct tm tm; int res=sscanf(str,"%d-%d-%dT%d:%d:%dZ",&tm.tm_year,&tm.tm_mon,&tm.tm_mday,&tm.tm_hour,&tm.tm_min,&tm.tm_sec); if (res != 6) return 0; tm.tm_year-=1900; tm.tm_mon-=1; #if defined(HAVE_API_WIN32_BASE) || defined(ANDROID) return 0; #else return timegm(&tm); #endif } static void osm_protobufdb_parse_info(struct osm_protobufdb_context *ctx, char *str) { char version[1024]; char changeset[1024]; char user[1024]; char uid[1024]; char timestamp[1024]; OSMPBF__Info *i=&ctx->i, ii=OSMPBF__INFO__INIT; *i=ii; if (osm_xml_get_attribute(str, "version", version, sizeof(version))) { i->version=atoi(version); i->has_version=1; } if (osm_xml_get_attribute(str, "changeset", changeset, sizeof(changeset))) { i->changeset=atoll(changeset); i->has_changeset=1; } if (osm_xml_get_attribute(str, "user", user, sizeof(user))) { osm_xml_decode_entities(user); i->user_sid=osm_protobufdb_string(ctx, user); i->has_user_sid=1; } if (osm_xml_get_attribute(str, "uid", uid, sizeof(uid))) { i->uid=atoi(uid); i->has_uid=1; } if (osm_xml_get_attribute(str, "timestamp", timestamp, sizeof(timestamp))) { i->timestamp=osm_protobufdb_timestamp(timestamp); i->has_timestamp=1; } } static int osm_protobufdb_parse_node(struct osm_protobufdb_context *ctx, char *str) { char id[1024]; char lat[1024]; char lon[1024]; OSMPBF__Node *n=&ctx->n, ni=OSMPBF__NODE__INIT; *n=ni; if (!osm_xml_get_attribute(str, "id", id, sizeof(id))) return 0; if (!osm_xml_get_attribute(str, "lat", lat, sizeof(lat))) return 0; if (!osm_xml_get_attribute(str, "lon", lon, sizeof(lon))) return 0; n->id=atoll(id); n->lat=atof(lat)*latlon_scale+0.5; n->lon=atof(lon)*latlon_scale+0.5; int file=n->id/db_config.node_ids_per_file; int fileo=n->id%db_config.node_ids_per_file; int blob=fileo/db_config.node_ids_per_blob; int blobo=fileo%db_config.node_ids_per_blob; int group=blobo/db_config.node_ids_per_group; osm_protobufdb_start_file(ctx, 1, file); osm_protobufdb_start_block(ctx, blob); osm_protobufdb_start_group(ctx, group); osm_protobufdb_parse_info(ctx, str); ctx->in_node=1; return 1; } static int osm_protobufdb_end_node(struct osm_protobufdb_context *ctx) { int p; p=osm_protobufdb_insert_node(ctx->n.id, ctx->pg); osm_protobufdb_modify_node(&ctx->n, &ctx->i, p, ctx->pg); ctx->in_node=0; return 1; } static int osm_protobufdb_parse_way(struct osm_protobufdb_context *ctx, char *str) { char id[1024]; OSMPBF__Way *w=&ctx->w, wi=OSMPBF__WAY__INIT; *w=wi; if (!osm_xml_get_attribute(str, "id", id, sizeof(id))) return 0; w->id=atoll(id); int file=w->id/db_config.way_ids_per_file; int fileo=w->id%db_config.way_ids_per_file; int blob=fileo/db_config.way_ids_per_blob; int blobo=fileo%db_config.way_ids_per_blob; int group=blobo/db_config.way_ids_per_group; osm_protobufdb_start_file(ctx, 2, file); osm_protobufdb_start_block(ctx, blob); osm_protobufdb_start_group(ctx, group); osm_protobufdb_parse_info(ctx, str); ctx->in_way=1; return 1; } static int osm_protobufdb_end_way(struct osm_protobufdb_context *ctx) { int p; p=osm_protobufdb_insert_way(ctx->w.id, ctx->pg); osm_protobufdb_modify_way(&ctx->w, &ctx->i, p, ctx->pg); ctx->in_way=0; return 1; } static int osm_protobufdb_parse_relation(struct osm_protobufdb_context *ctx, char *str) { char id[1024]; OSMPBF__Relation *r=&ctx->r, ri=OSMPBF__RELATION__INIT; *r=ri; if (!osm_xml_get_attribute(str, "id", id, sizeof(id))) return 0; r->id=atoll(id); int file=r->id/db_config.relation_ids_per_file; int fileo=r->id%db_config.relation_ids_per_file; int blob=fileo/db_config.relation_ids_per_blob; int blobo=fileo%db_config.relation_ids_per_blob; int group=blobo/db_config.relation_ids_per_group; osm_protobufdb_start_file(ctx, 3, file); osm_protobufdb_start_block(ctx, blob); osm_protobufdb_start_group(ctx, group); osm_protobufdb_parse_info(ctx, str); ctx->in_relation=1; return 1; } static int osm_protobufdb_end_relation(struct osm_protobufdb_context *ctx) { int p; p=osm_protobufdb_insert_relation(ctx->r.id, ctx->pg); osm_protobufdb_modify_relation(&ctx->r, &ctx->i, p, ctx->pg); ctx->in_node=0; return 1; } static int osm_protobufdb_parse_tag(struct osm_protobufdb_context *ctx, char *str) { OSMPBF__Node *n=&ctx->n; OSMPBF__Way *w=&ctx->w; OSMPBF__Relation *r=&ctx->r; char k_buffer[BUFFER_SIZE]; char v_buffer[BUFFER_SIZE]; if (!osm_xml_get_attribute(str, "k", k_buffer, BUFFER_SIZE)) return 0; if (!osm_xml_get_attribute(str, "v", v_buffer, BUFFER_SIZE)) return 0; osm_xml_decode_entities(v_buffer); if (ctx->in_node) { n->keys=g_realloc(n->keys, (n->n_keys+1)*sizeof(n->keys[0])); n->vals=g_realloc(n->vals, (n->n_vals+1)*sizeof(n->vals[0])); n->keys[n->n_keys++]=osm_protobufdb_string(ctx, k_buffer); n->vals[n->n_vals++]=osm_protobufdb_string(ctx, v_buffer); } if (ctx->in_way) { w->keys=g_realloc(w->keys, (w->n_keys+1)*sizeof(w->keys[0])); w->vals=g_realloc(w->vals, (w->n_vals+1)*sizeof(w->vals[0])); w->keys[w->n_keys++]=osm_protobufdb_string(ctx, k_buffer); w->vals[w->n_vals++]=osm_protobufdb_string(ctx, v_buffer); } if (ctx->in_relation) { r->keys=g_realloc(r->keys, (r->n_keys+1)*sizeof(r->keys[0])); r->vals=g_realloc(r->vals, (r->n_vals+1)*sizeof(r->vals[0])); r->keys[r->n_keys++]=osm_protobufdb_string(ctx, k_buffer); r->vals[r->n_vals++]=osm_protobufdb_string(ctx, v_buffer); } return 1; } static int osm_protobufdb_parse_nd(struct osm_protobufdb_context *ctx, char *str) { OSMPBF__Way *w=&ctx->w; char ref_buffer[BUFFER_SIZE]; if (!osm_xml_get_attribute(str, "ref", ref_buffer, BUFFER_SIZE)) return 0; if (ctx->in_way) { w->refs=g_realloc(w->refs, (w->n_refs+1)*sizeof(w->refs[0])); w->refs[w->n_refs++]=atoll(ref_buffer); } return 1; } static int osm_protobufdb_parse_member(struct osm_protobufdb_context *ctx, char *str) { OSMPBF__Relation *r=&ctx->r; char type_buffer[BUFFER_SIZE]; char ref_buffer[BUFFER_SIZE]; char role_buffer[BUFFER_SIZE]; int type=0; if (!osm_xml_get_attribute(str, "type", type_buffer, BUFFER_SIZE)) return 0; if (!osm_xml_get_attribute(str, "ref", ref_buffer, BUFFER_SIZE)) return 0; if (!osm_xml_get_attribute(str, "role", role_buffer, BUFFER_SIZE)) return 0; if (!g_strcmp0(type_buffer,"node")) type=0; else if (!g_strcmp0(type_buffer,"way")) type=1; else if (!g_strcmp0(type_buffer,"relation")) type=2; if (ctx->in_relation) { r->roles_sid=g_realloc(r->roles_sid, (r->n_roles_sid+1)*sizeof(r->roles_sid[0])); r->roles_sid[r->n_roles_sid++]=osm_protobufdb_string(ctx, role_buffer); r->memids=g_realloc(r->memids, (r->n_memids+1)*sizeof(r->memids[0])); r->memids[r->n_memids++]=atoll(ref_buffer); r->types=g_realloc(r->types, (r->n_types+1)*sizeof(r->types[0])); r->types[r->n_types++]=type; } return 1; } int osm_protobufdb_load(FILE *in, char *dir) { int size=BUFFER_SIZE; char buffer[size]; char *p; sig_alrm(0); test(); while (fgets(buffer, size, in)) { int closed=strstr(buffer,"/>")?1:0; p=strchr(buffer,'<'); if (! p) { fprintf(stderr,"WARNING: wrong line %s\n", buffer); continue; } if (!strncmp(p, "",7)) { osm_protobufdb_end_node(&context); } else if (!strncmp(p, "",6)) { osm_protobufdb_end_way(&context); } else if (!strncmp(p, "",11)) { osm_protobufdb_end_relation(&context); } else if (!strncmp(p, "",6)) { } else { fprintf(stderr,"WARNING: unknown tag in %s\n", buffer); } } finish(); return 1; }