diff options
Diffstat (limited to 'map')
53 files changed, 22050 insertions, 0 deletions
diff --git a/map/Makefile.am b/map/Makefile.am new file mode 100644 index 00000000..9de73d27 --- /dev/null +++ b/map/Makefile.am @@ -0,0 +1,11 @@ +SUBDIRS=mg textfile binfile shapefile +# garmin_img +if HAVELIBGARMIN +SUBDIRS+=garmin +endif +if MAP_POI_GEODOWNLOAD +SUBDIRS+=poi_geodownload +endif + +DIST_SUBDIRS=mg textfile poi_geodownload binfile garmin shapefile + diff --git a/map/binfile/Makefile.am b/map/binfile/Makefile.am new file mode 100644 index 00000000..e35e18bf --- /dev/null +++ b/map/binfile/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_binfile +if PLUGINS + modulemap_LTLIBRARIES = libmap_binfile.la +else + noinst_LTLIBRARIES = libmap_binfile.la +endif +libmap_binfile_la_SOURCES = binfile.c +libmap_binfile_la_LDFLAGS = -module -avoid-version diff --git a/map/binfile/binfile.c b/map/binfile/binfile.c new file mode 100644 index 00000000..4bf27909 --- /dev/null +++ b/map/binfile/binfile.c @@ -0,0 +1,1299 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "config.h" +#include "debug.h" +#include "plugin.h" +#include "projection.h" +#include "item.h" +#include "map.h" +#include "maptype.h" +#include "attr.h" +#include "coord.h" +#include "transform.h" +#include "file.h" +#include "zipfile.h" +#include "linguistics.h" +#include "endianess.h" + +static int map_id; + +struct tile { + int *start; + int *end; + int *pos; + int *pos_coord_start; + int *pos_coord; + int *pos_attr_start; + int *pos_attr; + int *pos_next; + int zipfile_num; + int mode; +}; + +struct map_priv { + int id; + char *filename; + char *cachedir; + struct file *fi; + struct zip_cd *index_cd; + int index_offset; + int cde_size; + struct zip_eoc *eoc; + int zip_members; + unsigned char *search_data; + int search_offset; + int search_size; + int version; + int check_version; + int map_version; + GHashTable *changes; +}; + +struct map_rect_priv { + int *start; + int *end; + enum attr_type attr_last; + int label; + int *label_attr[4]; + struct map_selection *sel; + struct map_priv *m; + struct item item; + int tile_depth; + struct tile tiles[8]; + struct tile *t; + int country_id; + char *url; +#ifdef DEBUG_SIZE + int size; +#endif +}; + +struct map_search_priv { + struct map_rect_priv *mr; + struct attr *search; + struct map_selection *ms; + int partial; + GHashTable *search_results; +}; + + +static void push_tile(struct map_rect_priv *mr, struct tile *t); +static void setup_pos(struct map_rect_priv *mr); + +static void lfh_to_cpu(struct zip_lfh *lfh) { + dbg_assert(lfh != NULL); + lfh->ziplocsig = le32_to_cpu(lfh->ziplocsig); + lfh->zipver = le16_to_cpu(lfh->zipver); + lfh->zipgenfld = le16_to_cpu(lfh->zipgenfld); + lfh->zipmthd = le16_to_cpu(lfh->zipmthd); + lfh->ziptime = le16_to_cpu(lfh->ziptime); + lfh->zipdate = le16_to_cpu(lfh->zipdate); + lfh->zipcrc = le32_to_cpu(lfh->zipcrc); + lfh->zipsize = le32_to_cpu(lfh->zipsize); + lfh->zipuncmp = le32_to_cpu(lfh->zipuncmp); + lfh->zipfnln = le16_to_cpu(lfh->zipfnln); + lfh->zipxtraln = le16_to_cpu(lfh->zipxtraln); +} + +static void cd_to_cpu(struct zip_cd *zcd) { + dbg_assert(zcd != NULL); + zcd->zipcensig = le32_to_cpu(zcd->zipcensig); + zcd->zipccrc = le32_to_cpu(zcd->zipccrc); + zcd->zipcsiz = le32_to_cpu(zcd->zipcsiz); + zcd->zipcunc = le32_to_cpu(zcd->zipcunc); + zcd->zipcfnl = le16_to_cpu(zcd->zipcfnl); + zcd->zipcxtl = le16_to_cpu(zcd->zipcxtl); + zcd->zipccml = le16_to_cpu(zcd->zipccml); + zcd->zipdsk = le16_to_cpu(zcd->zipdsk); + zcd->zipint = le16_to_cpu(zcd->zipint); + zcd->zipext = le32_to_cpu(zcd->zipext); + zcd->zipofst = le32_to_cpu(zcd->zipofst); +} + +static void eoc_to_cpu(struct zip_eoc *eoc) { + dbg_assert(eoc != NULL); + eoc->zipesig = le32_to_cpu(eoc->zipesig); + eoc->zipedsk = le16_to_cpu(eoc->zipedsk); + eoc->zipecen = le16_to_cpu(eoc->zipecen); + eoc->zipenum = le16_to_cpu(eoc->zipenum); + eoc->zipecenn = le16_to_cpu(eoc->zipecenn); + eoc->zipecsz = le32_to_cpu(eoc->zipecsz); + eoc->zipeofst = le32_to_cpu(eoc->zipeofst); + eoc->zipecoml = le16_to_cpu(eoc->zipecoml); +} + +static void binfile_check_version(struct map_priv *m); + +static struct zip_eoc * +binfile_read_eoc(struct file *fi) +{ + struct zip_eoc *eoc; + eoc=(struct zip_eoc *)file_data_read(fi,fi->size-sizeof(struct zip_eoc), sizeof(struct zip_eoc)); + if (eoc) { + eoc_to_cpu(eoc); + dbg(1,"sig 0x%x\n", eoc->zipesig); + if (eoc->zipesig != zip_eoc_sig) { + file_data_free(fi,(unsigned char *)eoc); + eoc=NULL; + } + } + return eoc; +} + +static struct zip_cd * +binfile_read_cd(struct map_priv *m, int offset, int len) +{ + struct zip_cd *cd; + if (len == -1) { + cd=(struct zip_cd *)file_data_read(m->fi,m->eoc->zipeofst+offset, sizeof(*cd)); + cd_to_cpu(m->index_cd); + len=cd->zipcfnl; + file_data_free(m->fi,(unsigned char *)cd); + } + cd=(struct zip_cd *)file_data_read(m->fi,m->eoc->zipeofst+offset, sizeof(*cd)+len); + if (cd) { + cd_to_cpu(cd); + dbg(1,"sig 0x%x\n", cd->zipcensig); + if (cd->zipcensig != zip_cd_sig) { + file_data_free(m->fi,(unsigned char *)cd); + cd=NULL; + } + } + return cd; +} + +static struct zip_lfh * +binfile_read_lfh(struct file *fi, unsigned int offset) +{ + struct zip_lfh *lfh; + + lfh=(struct zip_lfh *)(file_data_read(fi,offset,sizeof(struct zip_lfh))); + if (lfh) { + lfh_to_cpu(lfh); + if (lfh->ziplocsig != zip_lfh_sig) { + file_data_free(fi,(unsigned char *)lfh); + lfh=NULL; + } + } + return lfh; +} + +static unsigned char * +binfile_read_content(struct file *fi, int offset, struct zip_lfh *lfh) +{ + offset+=sizeof(struct zip_lfh)+lfh->zipfnln+lfh->zipxtraln; + switch (lfh->zipmthd) { + case 0: + return file_data_read(fi,offset, lfh->zipuncmp); + case 8: + return file_data_read_compressed(fi,offset, lfh->zipsize, lfh->zipuncmp); + default: + dbg(0,"Unknown compression method %d\n", lfh->zipmthd); + return NULL; + } +} + +static int +binfile_search_cd(struct map_priv *m, int offset, char *name, int partial, int skip) +{ + int size=4096; + int end=m->eoc->zipecsz; + int len=strlen(name); + struct zip_cd *cd; +#if 0 + dbg(0,"end=%d\n",end); +#endif + while (offset < end) { + cd=(struct zip_cd *)(m->search_data+offset-m->search_offset); + if (! m->search_data || + m->search_offset > offset || + offset-m->search_offset+sizeof(*cd) > m->search_size || + offset-m->search_offset+sizeof(*cd)+cd->zipcfnl > m->search_size + ) { +#if 0 + dbg(0,"reload %p %d %d\n", m->search_data, m->search_offset, offset); +#endif + if (m->search_data) + file_data_free(m->fi,m->search_data); + m->search_offset=offset; + m->search_size=end-offset; + if (m->search_size > size) + m->search_size=size; + m->search_data=file_data_read(m->fi,m->eoc->zipeofst+m->search_offset,m->search_size); + cd=(struct zip_cd *)m->search_data; + } +#if 0 + dbg(0,"offset=%d search_offset=%d search_size=%d search_data=%p cd=%p\n", offset, m->search_offset, m->search_size, m->search_data, cd); + dbg(0,"offset=%d fn='%s'\n",offset,cd->zipcfn); +#endif + if (!skip && + (partial || cd->zipcfnl == len) && + !strncmp(cd->zipcfn, name, len)) + return offset; + skip=0; + offset+=sizeof(*cd)+cd->zipcfnl+cd->zipcxtl+cd->zipccml; +; + } + return -1; +} + +static void +map_destroy_binfile(struct map_priv *m) +{ + dbg(1,"map_destroy_binfile\n"); + g_free(m); +} + +static void +binfile_coord_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + struct tile *t=mr->t; + t->pos_coord=t->pos_coord_start; +} + +static int +binfile_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + struct tile *t=mr->t; + int ret=0; + dbg(2,"binfile_coord_get %d\n",count); + while (count--) { + dbg(2,"%p vs %p\n", t->pos_coord, t->pos_attr_start); + if (t->pos_coord >= t->pos_attr_start) + break; + c->x=le32_to_cpu(*(t->pos_coord++)); + c->y=le32_to_cpu(*(t->pos_coord++)); + c++; + ret++; + } + return ret; +} + +static void +binfile_attr_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + struct tile *t=mr->t; + t->pos_attr=t->pos_attr_start; + mr->label=0; + memset(mr->label_attr, 0, sizeof(mr->label_attr)); + +} + +static char * +binfile_extract(struct map_priv *m, char *dir, char *filename, int partial) +{ + char *full,*fulld,*sep; + unsigned char *start; + int len,offset=m->index_offset; + struct zip_cd *cd; + struct zip_lfh *lfh; + FILE *f; + + for (;;) { + offset=binfile_search_cd(m, offset, filename, partial, 1); + if (offset == -1) + break; + cd=binfile_read_cd(m, offset, -1); + len=strlen(dir)+1+cd->zipcfnl+1; + full=g_malloc(len); + strcpy(full,dir); + strcpy(full+strlen(full),"/"); + strncpy(full+strlen(full),cd->zipcfn,cd->zipcfnl); + full[len-1]='\0'; + fulld=g_strdup(full); + sep=strrchr(fulld, '/'); + if (sep) { + *sep='\0'; + file_mkdir(fulld, 1); + } + if (full[len-2] != '/') { + lfh=binfile_read_lfh(m->fi, cd->zipofst); + start=binfile_read_content(m->fi, cd->zipofst, lfh); + dbg(0,"fopen '%s'\n", full); + f=fopen(full,"w"); + fwrite(start, lfh->zipuncmp, 1, f); + fclose(f); + file_data_free(m->fi, start); + file_data_free(m->fi, (unsigned char *)lfh); + } + file_data_free(m->fi, (unsigned char *)cd); + g_free(fulld); + g_free(full); + if (! partial) + break; + } + + return g_strdup_printf("%s/%s",dir,filename); +} + +static int +binfile_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + struct tile *t=mr->t; + enum attr_type type; + int i,size; + + if (attr_type != mr->attr_last) { + t->pos_attr=t->pos_attr_start; + mr->attr_last=attr_type; + } + while (t->pos_attr < t->pos_next) { + size=le32_to_cpu(*(t->pos_attr++)); + type=le32_to_cpu(t->pos_attr[0]); + if (type == attr_label) + mr->label=1; + if (type == attr_house_number) + mr->label_attr[0]=t->pos_attr; + if (type == attr_street_name) + mr->label_attr[1]=t->pos_attr; + if (type == attr_street_name_systematic) + mr->label_attr[2]=t->pos_attr; + if (type == attr_town_name && mr->item.type < type_line) + mr->label_attr[3]=t->pos_attr; + if (type == attr_type || attr_type == attr_any) { + if (attr_type == attr_any) { + dbg(1,"pos %p attr %s size %d\n", t->pos_attr-1, attr_to_name(type), size); + } + attr->type=type; + attr_data_set_le(attr, t->pos_attr+1); + if (type == attr_url_local) { + g_free(mr->url); + mr->url=binfile_extract(mr->m, mr->m->cachedir, attr->u.str, 1); + attr->u.str=mr->url; + } + if (type == attr_flags && mr->m->map_version < 1) + attr->u.num |= AF_CAR; + t->pos_attr+=size; + return 1; + } else { + t->pos_attr+=size; + } + } + if (!mr->label && (attr_type == attr_any || attr_type == attr_label)) { + for (i = 0 ; i < sizeof(mr->label_attr)/sizeof(int *) ; i++) { + if (mr->label_attr[i]) { + mr->label=1; + attr->type=attr_label; + attr_data_set_le(attr,mr->label_attr[i]+1); + return 1; + } + } + } + return 0; +} + +struct binfile_hash_entry { + struct item_id id; + int flags; + int data[0]; +}; + +static guint +binfile_hash_entry_hash(gconstpointer key) +{ + const struct binfile_hash_entry *entry=key; + return (entry->id.id_hi ^ entry->id.id_lo); +} + +static gboolean +binfile_hash_entry_equal(gconstpointer a, gconstpointer b) +{ + const struct binfile_hash_entry *entry1=a,*entry2=b; + return (entry1->id.id_hi==entry2->id.id_hi && entry1->id.id_lo == entry2->id.id_lo); +} + +static int * +binfile_item_dup(struct map_priv *m, struct item *item, struct tile *t, int extend) +{ + int size=le32_to_cpu(t->pos[0]); + struct binfile_hash_entry *entry=g_malloc(sizeof(struct binfile_hash_entry)+(size+1+extend)*sizeof(int)); + void *ret=entry->data; + entry->id.id_hi=item->id_hi; + entry->id.id_lo=item->id_lo; + entry->flags=1; + dbg(0,"id 0x%x,0x%x\n",entry->id.id_hi,entry->id.id_lo); + + memcpy(ret, t->pos, (size+1)*sizeof(int)); + if (!m->changes) + m->changes=g_hash_table_new_full(binfile_hash_entry_hash, binfile_hash_entry_equal, g_free, NULL); + g_hash_table_replace(m->changes, entry, entry); + dbg(0,"ret %p\n",ret); + return ret; +} + +static int +binfile_coord_set(void *priv_data, struct coord *c, int count, enum change_mode mode) +{ + struct map_rect_priv *mr=priv_data; + struct tile *t=mr->t,*tn,new; + int i,delta,move_len; + int write_offset,move_offset,aoffset,coffset,clen; + int *data; + + { + int *i=t->pos,j=0; + dbg(0,"Before: pos_coord=%d\n",t->pos_coord-i); + while (i < t->pos_next) + dbg(0,"%d:0x%x\n",j++,*i++); + + } + aoffset=t->pos_attr-t->pos_attr_start; + coffset=t->pos_coord-t->pos_coord_start-2; + clen=t->pos_attr_start-t->pos_coord+2; + dbg(0,"coffset=%d clen=%d\n",coffset,clen); + switch (mode) { + case change_mode_delete: + if (count*2 > clen) + count=clen/2; + delta=-count*2; + move_offset=coffset+count*2; + move_len=t->pos_next-t->pos_coord_start-move_offset; + write_offset=0; + break; + case change_mode_modify: + write_offset=coffset; + if (count*2 > clen) { + delta=count*2-clen; + move_offset=t->pos_attr_start-t->pos_coord_start; + move_len=t->pos_next-t->pos_coord_start-move_offset; + } else { + move_len=0; + move_offset=0; + delta=0; + } + break; + case change_mode_prepend: + delta=count*2; + move_offset=coffset-2; + move_len=t->pos_next-t->pos_coord_start-move_offset; + write_offset=coffset-2; + break; + case change_mode_append: + delta=count*2; + move_offset=coffset; + move_len=t->pos_next-t->pos_coord_start-move_offset; + write_offset=coffset; + break; + default: + return 0; + } + dbg(0,"delta %d\n",delta); + data=binfile_item_dup(mr->m, &mr->item, t, delta > 0 ? delta:0); + data[0]=cpu_to_le32(le32_to_cpu(data[0])+delta); + data[2]=cpu_to_le32(le32_to_cpu(data[2])+delta); + new.pos=new.start=data; + new.zipfile_num=t->zipfile_num; + new.mode=2; + push_tile(mr, &new); + setup_pos(mr); + tn=mr->t; + tn->pos_coord=tn->pos_coord_start+coffset; + tn->pos_attr=tn->pos_attr_start+aoffset; + dbg(0,"moving %d ints from offset %d to %d\n",move_len,tn->pos_coord_start+move_offset-data,tn->pos_coord_start+move_offset+delta-data); + memmove(tn->pos_coord_start+move_offset+delta, tn->pos_coord_start+move_offset, move_len*4); + { + int *i=tn->pos,j=0; + dbg(0,"After move: pos_coord=%d\n",tn->pos_coord-i); + while (i < tn->pos_next) + dbg(0,"%d:0x%x\n",j++,*i++); + } + if (mode != change_mode_append) + tn->pos_coord+=move_offset; + if (mode != change_mode_delete) { + dbg(0,"writing %d ints at offset %d\n",count*2,write_offset+tn->pos_coord_start-data); + for (i = 0 ; i < count ; i++) { + tn->pos_coord_start[write_offset++]=c[i].x; + tn->pos_coord_start[write_offset++]=c[i].y; + } + + } + { + int *i=tn->pos,j=0; + dbg(0,"After: pos_coord=%d\n",tn->pos_coord-i); + while (i < tn->pos_next) + dbg(0,"%d:0x%x\n",j++,*i++); + } + return 1; +} + +static int +binfile_attr_set(void *priv_data, struct attr *attr, enum change_mode mode) +{ + struct map_rect_priv *mr=priv_data; + struct tile *t=mr->t,*tn,new; + int extend,offset,delta,move_len; + int write_offset,move_offset,naoffset,coffset,oattr_len; + int nattr_size,nattr_len,pad; + int *data; + + { + int *i=t->pos,j=0; + dbg(0,"Before: pos_attr=%d\n",t->pos_attr-i); + while (i < t->pos_next) + dbg(0,"%d:0x%x\n",j++,*i++); + + } + + write_offset=0; + naoffset=t->pos_attr-t->pos_attr_start; + coffset=t->pos_coord-t->pos_coord_start; + offset=0; + oattr_len=0; + if (!naoffset) { + if (mode == change_mode_delete || mode == change_mode_modify) { + dbg(0,"no attribute selected\n"); + return 0; + } + if (mode == change_mode_append) + naoffset=t->pos_next-t->pos_attr_start; + } + while (offset < naoffset) { + oattr_len=le32_to_cpu(t->pos_attr_start[offset])+1; + dbg(0,"len %d\n",oattr_len); + write_offset=offset; + offset+=oattr_len; + } + move_len=t->pos_next-t->pos_attr_start-offset; + move_offset=offset; + switch (mode) { + case change_mode_delete: + nattr_size=0; + nattr_len=0; + pad=0; + extend=0; + break; + case change_mode_modify: + case change_mode_prepend: + case change_mode_append: + nattr_size=attr_data_size(attr); + pad=(4-(nattr_size%4))%4; + nattr_len=(nattr_size+pad)/4+2; + if (mode == change_mode_prepend) { + move_offset=write_offset; + move_len+=oattr_len; + } + if (mode == change_mode_append) { + write_offset=move_offset; + } + break; + default: + return 0; + } + if (mode == change_mode_delete || mode == change_mode_modify) + delta=nattr_len-oattr_len; + else + delta=nattr_len; + dbg(0,"delta %d oattr_len %d nattr_len %d\n",delta,oattr_len, nattr_len); + data=binfile_item_dup(mr->m, &mr->item, t, delta > 0 ? delta:0); + data[0]=cpu_to_le32(le32_to_cpu(data[0])+delta); + new.pos=new.start=data; + new.zipfile_num=t->zipfile_num; + new.mode=2; + push_tile(mr, &new); + setup_pos(mr); + tn=mr->t; + tn->pos_coord=tn->pos_coord_start+coffset; + tn->pos_attr=tn->pos_attr_start+offset; + dbg(0,"attr start %d offset %d\n",tn->pos_attr_start-data,offset); + dbg(0,"moving %d ints from offset %d to %d\n",move_len,tn->pos_attr_start+move_offset-data,tn->pos_attr_start+move_offset+delta-data); + memmove(tn->pos_attr_start+move_offset+delta, tn->pos_attr_start+move_offset, move_len*4); + if (mode != change_mode_append) + tn->pos_attr+=delta; + { + int *i=tn->pos,j=0; + dbg(0,"After move: pos_attr=%d\n",tn->pos_attr-i); + while (i < tn->pos_next) + dbg(0,"%d:0x%x\n",j++,*i++); + } + if (nattr_len) { + int *nattr=tn->pos_attr_start+write_offset; + dbg(0,"writing %d ints at %d\n",nattr_len,nattr-data); + nattr[0]=cpu_to_le32(nattr_len-1); + nattr[1]=cpu_to_le32(attr->type); + memcpy(nattr+2, attr_data_get(attr), nattr_size); + memset((unsigned char *)(nattr+2)+nattr_size, 0, pad); + } + { + int *i=tn->pos,j=0; + dbg(0,"After: pos_attr=%d\n",tn->pos_attr-i); + while (i < tn->pos_next) + dbg(0,"%d:0x%x\n",j++,*i++); + } + return 1; +} + +static struct item_methods methods_binfile = { + binfile_coord_rewind, + binfile_coord_get, + binfile_attr_rewind, + binfile_attr_get, + NULL, + binfile_attr_set, + binfile_coord_set, +}; + +static void +push_tile(struct map_rect_priv *mr, struct tile *t) +{ + dbg_assert(mr->tile_depth < 8); + mr->t=&mr->tiles[mr->tile_depth++]; + *(mr->t)=*t; + mr->t->pos=mr->t->pos_next=mr->t->start; +} + +static int +pop_tile(struct map_rect_priv *mr) +{ + if (mr->tile_depth <= 1) + return 0; + if (mr->t->mode < 2) + file_data_free(mr->m->fi, (unsigned char *)(mr->t->start)); + mr->t=&mr->tiles[--mr->tile_depth-1]; + return 1; +} + + +static int +zipfile_to_tile(struct file *f, struct zip_cd *cd, struct tile *t) +{ + char buffer[1024]; + struct zip_lfh *lfh; + char *zipfn; + dbg(1,"enter %p %p %p\n", f, cd, t); + dbg(1,"cd->zipofst=0x%x\n", cd->zipofst); + t->start=NULL; + t->mode=1; + lfh=binfile_read_lfh(f, cd->zipofst); + zipfn=(char *)(file_data_read(f,cd->zipofst+sizeof(struct zip_lfh), lfh->zipfnln)); + strncpy(buffer, zipfn, lfh->zipfnln); + buffer[lfh->zipfnln]='\0'; + t->start=(int *)binfile_read_content(f, cd->zipofst, lfh); + t->end=t->start+lfh->zipuncmp/4; + dbg(1,"0x%x '%s' %d %d,%d\n", lfh->ziplocsig, buffer, sizeof(*cd)+cd->zipcfnl, lfh->zipsize, lfh->zipuncmp); + file_data_free(f, (unsigned char *)zipfn); + file_data_free(f, (unsigned char *)lfh); + return t->start != NULL; +} + +static void +push_zipfile_tile(struct map_rect_priv *mr, int zipfile) +{ + struct map_priv *m=mr->m; + struct file *f=m->fi; + struct tile t; + struct zip_cd *cd=(struct zip_cd *)(file_data_read(f, m->eoc->zipeofst + zipfile*m->cde_size, sizeof(struct zip_cd))); + cd_to_cpu(cd); + dbg(1,"enter %p %d\n", mr, zipfile); +#ifdef DEBUG_SIZE + mr->size+=cd->zipcunc; +#endif + t.zipfile_num=zipfile; + if (zipfile_to_tile(f, cd, &t)) + push_tile(mr, &t); + file_data_free(f, (unsigned char *)cd); +} + +static struct map_rect_priv * +map_rect_new_binfile(struct map_priv *map, struct map_selection *sel) +{ + struct map_rect_priv *mr; + struct tile t={}; + + binfile_check_version(map); + dbg(1,"map_rect_new_binfile\n"); + mr=g_new0(struct map_rect_priv, 1); + mr->m=map; + mr->sel=sel; + mr->item.id_hi=0; + mr->item.id_lo=0; + dbg(1,"zip_members=%d\n", map->zip_members); + if (map->eoc) + push_zipfile_tile(mr, map->zip_members-1); + else { + unsigned char *d=file_data_read(map->fi, 0, map->fi->size); + t.start=(int *)d; + t.end=(int *)(d+map->fi->size); + t.zipfile_num=0; + t.mode=0; + push_tile(mr, &t); + } + mr->item.meth=&methods_binfile; + mr->item.priv_data=mr; + return mr; +} + +static void +write_changes_do(gpointer key, gpointer value, gpointer user_data) +{ + struct binfile_hash_entry *entry=key; + FILE *out=user_data; + if (entry->flags) { + entry->flags=0; + fwrite(entry, sizeof(*entry)+(le32_to_cpu(entry->data[0])+1)*4, 1, out); + dbg(0,"yes\n"); + } +} + +static void +write_changes(struct map_priv *m) +{ + FILE *changes; + char *changes_file; + if (!m->changes) + return; + changes_file=g_strdup_printf("%s.log",m->filename); + changes=fopen(changes_file,"ab"); + g_hash_table_foreach(m->changes, write_changes_do, changes); + fclose(changes); + g_free(changes_file); +} + +static void +load_changes(struct map_priv *m) +{ + FILE *changes; + char *changes_file; + struct binfile_hash_entry entry,*e; + int size; + changes_file=g_strdup_printf("%s.log",m->filename); + changes=fopen(changes_file,"rb"); + if (! changes) + return; + m->changes=g_hash_table_new_full(binfile_hash_entry_hash, binfile_hash_entry_equal, g_free, NULL); + while (fread(&entry, sizeof(entry), 1, changes) == 1) { + if (fread(&size, sizeof(size), 1, changes) != 1) + break; + e=g_malloc(sizeof(struct binfile_hash_entry)+(le32_to_cpu(size)+1)*4); + *e=entry; + e->data[0]=size; + if (fread(e->data+1, le32_to_cpu(size)*4, 1, changes) != 1) + break; + g_hash_table_replace(m->changes, e, e); + } + fclose(changes); + g_free(changes_file); +} + + +static void +map_rect_destroy_binfile(struct map_rect_priv *mr) +{ + write_changes(mr->m); + while (pop_tile(mr)); +#ifdef DEBUG_SIZE + dbg(0,"size=%d kb\n",mr->size/1024); +#endif + file_data_free(mr->m->fi, (unsigned char *)(mr->tiles[0].start)); + g_free(mr->url); + g_free(mr); +} + +static void +setup_pos(struct map_rect_priv *mr) +{ + int size,coord_size; + struct tile *t=mr->t; + size=le32_to_cpu(t->pos[0]); + if (size > 1024*1024 || size < 0) { + dbg(0,"size=0x%x\n", size); +#if 0 + fprintf(stderr,"offset=%d\n", (unsigned char *)(mr->pos)-mr->m->f->begin); +#endif + dbg(0,"size error"); + } + t->pos_next=t->pos+size+1; + mr->item.type=le32_to_cpu(t->pos[1]); + coord_size=le32_to_cpu(t->pos[2]); + t->pos_coord_start=t->pos+3; + t->pos_attr_start=t->pos_coord_start+coord_size; +} + +static int +selection_contains(struct map_selection *sel, struct coord_rect *r, struct range *mima) +{ + int order; + if (! sel) + return 1; + while (sel) { + if (coord_rect_overlap(r, &sel->u.c_rect)) { + order=sel->order; + dbg(1,"min %d max %d order %d\n", mima->min, mima->max, order); + if (!mima->min && !mima->max) + return 1; + if (order >= mima->min && order <= mima->max) + return 1; + } + sel=sel->next; + } + return 0; +} + +static void +map_parse_country_binfile(struct map_rect_priv *mr) +{ + struct attr at; + if (binfile_attr_get(mr->item.priv_data, attr_country_id, &at)) { + if (at.u.num == mr->country_id) + { + if (binfile_attr_get(mr->item.priv_data, attr_zipfile_ref, &at)) + { + push_zipfile_tile(mr, at.u.num); + } + } + } +} + +static void +map_parse_submap(struct map_rect_priv *mr) +{ + struct coord_rect r; + struct coord c[2]; + struct attr at; + if (binfile_coord_get(mr->item.priv_data, c, 2) != 2) + return; + r.lu.x=c[0].x; + r.lu.y=c[1].y; + r.rl.x=c[1].x; + r.rl.y=c[0].y; + if (!binfile_attr_get(mr->item.priv_data, attr_order, &at)) + return; + if (!mr->m->eoc || !selection_contains(mr->sel, &r, &at.u.range)) + return; + if (!binfile_attr_get(mr->item.priv_data, attr_zipfile_ref, &at)) + return; + dbg(1,"pushing zipfile %d from %d\n", at.u.num, mr->t->zipfile_num); + push_zipfile_tile(mr, at.u.num); +} + +static int +push_modified_item(struct map_rect_priv *mr) +{ + struct item_id id; + struct binfile_hash_entry *entry; + id.id_hi=mr->item.id_hi; + id.id_lo=mr->item.id_lo; + entry=g_hash_table_lookup(mr->m->changes, &id); + if (entry) { + struct tile tn; + tn.pos_next=tn.pos=tn.start=entry->data; + tn.zipfile_num=mr->item.id_hi; + tn.mode=2; + tn.end=tn.start+le32_to_cpu(entry->data[0])+1; + push_tile(mr, &tn); + return 1; + } + return 0; +} + +static struct item * +map_rect_get_item_binfile(struct map_rect_priv *mr) +{ + struct tile *t; + for (;;) { + t=mr->t; + if (! t) + return NULL; + t->pos=t->pos_next; + if (t->pos >= t->end) { + if (pop_tile(mr)) + continue; + return NULL; + } + setup_pos(mr); + binfile_coord_rewind(mr); + binfile_attr_rewind(mr); + if ((mr->item.type == type_submap) && (!mr->country_id)) { + map_parse_submap(mr); + continue; + } + if (t->mode != 2) { + mr->item.id_hi=t->zipfile_num; + mr->item.id_lo=t->pos-t->start; + if (mr->m->changes && push_modified_item(mr)) + continue; + } + if (mr->country_id) + { + if (mr->item.type == type_countryindex) { + map_parse_country_binfile(mr); + } + if (item_is_town(mr->item)) + { + return &mr->item; + } else { + continue; + } + } + return &mr->item; + } +} + +static struct item * +map_rect_get_item_byid_binfile(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + struct tile *t; + if (mr->m->eoc) + push_zipfile_tile(mr, id_hi); + t=mr->t; + t->pos=t->start+id_lo; + mr->item.id_hi=id_hi; + mr->item.id_lo=id_lo; + if (mr->m->changes) + push_modified_item(mr); + setup_pos(mr); + binfile_coord_rewind(mr); + binfile_attr_rewind(mr); + return &mr->item; +} + +static struct map_search_priv * +binmap_search_new(struct map_priv *map, struct item *item, struct attr *search, int partial) +{ + struct map_rect_priv *map_rec; + struct map_search_priv *msp; + struct map_selection *ms; + struct item *town; + + /* + * NOTE: If you implement search for other attributes than attr_town_name and attr_street_name, + * please update this comment and the documentation for map_search_new() in map.c + */ + switch (search->type) { + case attr_country_name: + break; + case attr_town_name: + case attr_town_or_district_name: + msp = g_new(struct map_search_priv, 1); + msp->search_results = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + map_rec = map_rect_new_binfile(map, NULL); + map_rec->country_id = item->id_lo; + msp->mr = map_rec; + msp->search = search; + msp->partial = partial; + return msp; + break; + case attr_town_postal: + break; + case attr_street_name: + if (! item->map) + break; + if (!map_priv_is(item->map, map)) + break; + ms = g_new(struct map_selection, 1); + ms->next = NULL; + ms->range = item_range_all; /* FIXME */ + ms->order = 18; + map_rec = map_rect_new_binfile(map, ms); + town = map_rect_get_item_byid_binfile(map_rec, item->id_hi, item->id_lo); + if (town) { + struct map_search_priv *msp = g_new(struct map_search_priv, 1); + struct coord *c = g_new(struct coord, 1); + int size = 10000; + switch (town->type) { + case type_town_label_2e5: + size = 10000; + break; + case type_town_label_2e4: + size = 5000; + break; + case type_town_label_2e3: + size = 2500; + break; + case type_town_label_2e2: + size = 1000; + break; + default: + break; + } + item_coord_get(town, c, 1); + ms->u.c_rect.lu.x = c->x-size; + ms->u.c_rect.lu.y = c->y+size; + ms->u.c_rect.rl.x = c->x+size; + ms->u.c_rect.rl.y = c->y-size; + + map_rect_destroy_binfile(map_rec); + map_rec = map_rect_new_binfile(map, ms); + msp->mr = map_rec; + msp->search = search; + msp->partial = partial; + msp->search_results = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + return msp; + } + map_rect_destroy_binfile(map_rec); + g_free(ms); + break; + default: + break; + } + return NULL; +} +static int +ascii_cmp(char *name, char *match, int partial) +{ + if (partial) + return g_ascii_strncasecmp(name, match, strlen(match)); + else + return g_ascii_strcasecmp(name, match); +} + +static struct item * +binmap_search_get_item(struct map_search_priv *map_search) +{ + struct item* it; + while ((it = map_rect_get_item_binfile(map_search->mr))) { + switch (map_search->search->type) { + case attr_town_name: + case attr_district_name: + case attr_town_or_district_name: + if (item_is_town(*it) && map_search->search->type != attr_district_name) { + struct attr at; + if (binfile_attr_get(it->priv_data, attr_town_name_match, &at) || binfile_attr_get(it->priv_data, attr_town_name, &at)) { + if (!ascii_cmp(at.u.str, map_search->search->u.str, map_search->partial)) { + return it; + } + } + } + if (item_is_district(*it) && map_search->search->type != attr_town_name) { + struct attr at; + if (binfile_attr_get(it->priv_data, attr_district_name_match, &at) || binfile_attr_get(it->priv_data, attr_district_name, &at)) { + if (!ascii_cmp(at.u.str, map_search->search->u.str, map_search->partial)) { + return it; + } + } + } + break; + case attr_street_name: + if ((it->type == type_street_3_city) || (it->type == type_street_2_city) || (it->type == type_street_1_city)) { + struct attr at; + if (map_selection_contains_item_rect(map_search->mr->sel, it) && binfile_attr_get(it->priv_data, attr_label, &at)) { + int i,match=0; + char *str=g_strdup(at.u.str); + char *word=str; + do { + for (i = 0 ; i < 3 ; i++) { + char *name=linguistics_expand_special(word,i); + if (name && !ascii_cmp(name, map_search->search->u.str, map_search->partial)) + match=1; + g_free(name); + if (match) + break; + } + if (match) + break; + word=linguistics_next_word(word); + } while (word); + g_free(str); + if (match && !g_hash_table_lookup(map_search->search_results, at.u.str)) { + item_coord_rewind(it); + item_attr_rewind(it); + g_hash_table_insert(map_search->search_results, g_strdup(at.u.str), ""); + return it; + } + } + } + break; + default: + return NULL; + } + } + return NULL; +} + +static void +binmap_search_destroy(struct map_search_priv *ms) +{ + g_hash_table_destroy(ms->search_results); + g_free(ms->mr->sel); + map_rect_destroy_binfile(ms->mr); + g_free(ms); +} + +static struct map_methods map_methods_binfile = { + projection_mg, + "utf-8", + map_destroy_binfile, + map_rect_new_binfile, + map_rect_destroy_binfile, + map_rect_get_item_binfile, + map_rect_get_item_byid_binfile, + binmap_search_new, + binmap_search_destroy, + binmap_search_get_item +}; + +static int +binfile_get_index(struct map_priv *m) +{ + int len; + int cde_index_size; + int offset; + struct zip_cd *cd; + + len = strlen("index"); + cde_index_size = sizeof(struct zip_cd)+len; + offset = m->eoc->zipecsz-cde_index_size; + cd = binfile_read_cd(m, offset, len); + + if (cd) { + if (cd->zipcfnl == len && !strncmp(cd->zipcfn, "index", len)) { + m->index_offset=offset; + m->index_cd=cd; + return 1; + } + } + offset=binfile_search_cd(m, 0, "index", 0, 0); + if (offset == -1) + return 0; + cd=binfile_read_cd(m, offset, len); + if (!cd) + return 0; + m->index_offset=offset; + m->index_cd=cd; + return 1; +} + +static int +map_binfile_open(struct map_priv *m) +{ + int *magic; + struct zip_cd *first_cd; + struct map_rect_priv *mr; + struct item *item; + struct attr attr; + + dbg(1,"file_create %s\n", m->filename); + m->fi=file_create(m->filename); + if (! m->fi) { + dbg(0,"Failed to load '%s'\n", m->filename); + return 0; + } + if (m->check_version) + m->version=file_version(m->fi, m->check_version == 2); + magic=(int *)file_data_read(m->fi, 0, 4); + *magic = le32_to_cpu(*magic); + if (*magic == zip_lfh_sig) { + if ((m->eoc=binfile_read_eoc(m->fi)) && binfile_get_index(m) && (first_cd=binfile_read_cd(m, 0, 0))) { + m->cde_size=sizeof(struct zip_cd)+first_cd->zipcfnl; + m->zip_members=m->index_offset/m->cde_size+1; + dbg(1,"cde_size %d\n", m->cde_size); + dbg(1,"members %d\n",m->zip_members); + file_data_free(m->fi, (unsigned char *)first_cd); + } else { + dbg(0,"invalid file format for '%s'\n", m->filename); + return 0; + } + } else + file_mmap(m->fi); + file_data_free(m->fi, (unsigned char *)magic); + m->cachedir="/tmp/navit"; + m->map_version=0; + mr=map_rect_new_binfile(m, NULL); + if (mr) { + item=map_rect_get_item_binfile(mr); + if (item && item->type == type_map_information) + if (binfile_attr_get(item->priv_data, attr_version, &attr)) + m->map_version=attr.u.num; + map_rect_destroy_binfile(mr); + if (m->map_version >= 16) { + dbg(0,"Warning: This map is incompatible with your navit version. Please update navit.\n"); + return 0; + } + } + return 1; +} + +static void +map_binfile_close(struct map_priv *m) +{ + file_data_free(m->fi, (unsigned char *)m->index_cd); + file_data_free(m->fi, (unsigned char *)m->eoc); + file_destroy(m->fi); +} + +static void +map_binfile_destroy(struct map_priv *m) +{ + g_free(m->filename); + g_free(m); +} + + +static void +binfile_check_version(struct map_priv *m) +{ + int version; + if (!m->check_version) + return; + version=file_version(m->fi, m->check_version == 2); + if (version != m->version) { + map_binfile_close(m); + map_binfile_open(m); + } +} + + +static struct map_priv * +map_new_binfile(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + struct attr *data=attr_search(attrs, NULL, attr_data); + struct attr *check_version; + struct file_wordexp *wexp; + char **wexp_data; + if (! data) + return NULL; + + wexp=file_wordexp_new(data->u.str); + wexp_data=file_wordexp_get_array(wexp); + dbg(1,"map_new_binfile %s\n", data->u.str); + *meth=map_methods_binfile; + + m=g_new0(struct map_priv, 1); + m->id=++map_id; + m->filename=g_strdup(wexp_data[0]); + file_wordexp_destroy(wexp); + check_version=attr_search(attrs, NULL, attr_check_version); + if (check_version) + m->check_version=check_version->u.num; + if (!map_binfile_open(m)) { + map_binfile_destroy(m); + m=NULL; + } else { + load_changes(m); + } + return m; +} + +void +plugin_init(void) +{ + dbg(1,"binfile: plugin_init\n"); + plugin_register_map_type("binfile", map_new_binfile); +} + diff --git a/map/garmin/Makefile.am b/map/garmin/Makefile.am new file mode 100644 index 00000000..be15a148 --- /dev/null +++ b/map/garmin/Makefile.am @@ -0,0 +1,33 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_garmin +AM_CPPFLAGS+= @LIBGARMIN_CFLAGS@ +INCLUDES=-I$(top_srcdir)/navit/map/garmin/libgarmin +AM_CPPFLAGS+=-I$(top_srcdir)/navit/map/garmin/libgarmin +AM_CPPFLAGS+=-I$(srcdir) + +modulemap_LTLIBRARIES = libmap_garmin.la +libmap_garmin_la_SOURCES = garmin.c garmin.h gar2navit.c gar2navit.h +libmap_garmin_la_LIBADD = @LIBGARMIN_LIBS@ +libmap_garmin_la_LDFLAGS = -module -avoid-version +nodist_libmap_garmin_la_SOURCES = g2nbuiltin.h +builddir = @builddir@ + +gar2navit.l$(OBJEXT): g2nbuiltin.h + +CLEANFILES = g2nbuiltin.h + +noinst_PROGRAMS=gentypes$(EXEEXT) +gentypes_SOURCES=gentypes.c + +gentypes.$(OBJEXT): gentypes.c + $(CC_FOR_BUILD) -I$(top_srcdir)/navit -c -o $@ $< + +gentypes$(EXEEXT): $(gentypes_OBJECTS) $(gentypes_DEPENDENCIES) + @rm -f gentypes$(EXEEXT) + $(CCLD_FOR_BUILD) -o $@ $(gentypes_OBJECTS) + +g2nbuiltin.h: gentypes$(EXEEXT) garmintypes.txt + ./gentypes$(EXEEXT) $(top_srcdir)/navit/map/garmin/garmintypes.txt $@ + +EXTRA_DIST = garmintypes.txt + diff --git a/map/garmin/gar2navit.c b/map/garmin/gar2navit.c new file mode 100644 index 00000000..7c445040 --- /dev/null +++ b/map/garmin/gar2navit.c @@ -0,0 +1,219 @@ +/* + Copyright (C) 2007 Alexander Atanasov <aatanasov@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 + + Garmin and MapSource are registered trademarks or trademarks + of Garmin Ltd. or one of its subsidiaries. + +*/ + +/* +File format is: + +POINT +GROUP,0x0100 = town_label_1e5, Megapolis (10M +) +GROUP,0x0200 = town_label_5e4, Megapolis (5-10M) +... +GROUP,0x1e00-0x1e3f = district_label, District, Province, State Name +... +POLYLINE +GROUP,0x00 = ALL, street_1_land, Road +GROUP,0x01 = MCTL, highway_land, Major HWY thick +GROUP,0x02 = MCTL, street_4_land, Principal HWY-thick +GROUP,0x03 = MCTL, street_2_land, Principal HWY-medium +.... +POLYGONE +GROUP,0x01 = town_poly, City (>200k) +GROUP,0x02 = town_poly, City (<200k) +GROUP,0x03 = town_poly, Village + +GROUP is +0 - RGN1 +1 - RGN2-4 + */ + +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include "item.h" +#include "attr.h" +#include "garmin.h" +#include "gar2navit.h" + +static int add_def(struct gar2nav_conv *conv, int type, unsigned short minid, + unsigned short maxid, unsigned int group, char *ntype, + char *descr) +{ + enum item_type it; + struct gar2navit *g2n; + dlog(11, "group=%d type=%d routable=%u min=%04X max=%04X ntype=%s descr=%s\n", + group, type, minid, maxid, ntype, descr); + it = item_from_name(ntype); + if (it==type_none) { + dlog(1, "Please define: %s\n", ntype); + } + g2n = calloc(1, sizeof(*g2n)); + if (!g2n) + return -1; + g2n->id = minid; + g2n->maxid = maxid; + g2n->ntype = it; + g2n->descr = strdup(descr); + g2n->group = group; + if (type == 1) { + g2n->next = conv->points; + conv->points = g2n; + } else if (type == 2) { + g2n->next = conv->polylines; + conv->polylines = g2n; + } else if (type == 3) { + g2n->next = conv->polygons; + conv->polygons = g2n; + } + return 0; +} + +static int load_types_file(char *file, struct gar2nav_conv *conv) +{ + char buf[4096]; + char descr[4096]; + char ntype[4096]; + FILE *fp; + unsigned int minid, maxid, group; + int rc; + int type = -1; + + fp = fopen(file, "r"); + if (!fp) + return -1; + while (fgets(buf, sizeof(buf), fp)) { + if (*buf == '#' || *buf == '\n') + continue; + if (!strncasecmp(buf, "POINT", 5)) { + type = 1; + continue; + } else if (!strncasecmp(buf, "POI", 3)) { + type = 1; + continue; + } else if (!strncasecmp(buf, "POLYLINE", 8)) { + type = 2; + continue; + } else if (!strncasecmp(buf, "POLYGONE", 8)) { + type = 3; + continue; + } + // assume only lines are routable + rc = sscanf(buf, "%d, 0x%04X - 0x%04X = %[^\t , ] , %[^\n]", + &group, &minid, &maxid, ntype, descr); + if (rc != 5) { + maxid = 0; + rc = sscanf(buf, "%d,0x%04X = %[^\t, ], %[^\n]", + &group, &minid, ntype, descr); + if (rc != 4) { + dlog(1, "Invalid line rc=%d:[%s]\n",rc, buf); + dlog(1, "minid=%04X ntype=[%s] des=[%s]\n", + minid, ntype, descr); + continue; + } + } + add_def(conv, type, minid, maxid, group, ntype, descr); + } + fclose(fp); + return 1; +} + +struct gar2nav_conv *g2n_conv_load(char *file) +{ + struct gar2nav_conv *c; + int rc; + + c = calloc(1, sizeof(*c)); + if (!c) + return c; + rc = load_types_file(file, c); + if (rc < 0) { + dlog(1, "Failed to load: [%s]\n", file); + free(c); + return NULL; + } + return c; +} + +enum item_type g2n_get_type(struct gar2nav_conv *c, unsigned int type, unsigned short id) +{ + struct gar2navit *def = NULL; + int group; + group = (type >> G2N_KIND_SHIFT); + type &= ~G2N_KIND_MASK; + if (type == G2N_POINT) + def = c->points; + else if (type == G2N_POLYLINE) + def = c->polylines; + else if (type == G2N_POLYGONE) + def = c->polygons; + else { + dlog(1, "Unknown conversion type:%d\n", type); + return type_none; + } + + if (!def) { + dlog(5, "No conversion data for %d\n", type); + return type_none; + } + + while (def) { + if (def->group == group && + ((!def->maxid && def->id == id) || + (def->id <= id && id <= def->maxid))) + return def->ntype; + def = def->next; + } + dlog(5, "Type[%d]:ID:[%04X] unknown\n", type, id); + return type == G2N_POINT ? type_point_unkn : type_street_unkn; +} + +char *g2n_get_descr(struct gar2nav_conv *c, int type, unsigned short id) +{ + struct gar2navit *def = NULL; + if (type == G2N_POINT) + def = c->points; + else if (type == G2N_POLYLINE) + def = c->polylines; + else if (type == G2N_POLYGONE) + def = c->polygons; + else { + dlog(1, "Unknown conversion type:%d\n", type); + return NULL; + } + while (def) { + if ((!def->maxid && def->id == id) || + (def->id <= id && id <= def->maxid)) + return def->descr; + def = def->next; + } + dlog(5, "Type[%d]:ID:[%04X] unknown\n", type, id); + return NULL; +} + +#if 0 +int main(int argc, char **argv) +{ + load_types_file(argv[1], NULL); + return 0; +} +#endif + +#include "g2nbuiltin.h" diff --git a/map/garmin/gar2navit.h b/map/garmin/gar2navit.h new file mode 100644 index 00000000..27c5f65e --- /dev/null +++ b/map/garmin/gar2navit.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2007 Alexander Atanasov <aatanasov@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 + + Garmin and MapSource are registered trademarks or trademarks + of Garmin Ltd. or one of its subsidiaries. + +*/ + +#define GROUP_RGN1 0 +#define GROUP_RGN2 1 + +struct gar2navit { + unsigned short id; + unsigned short maxid; + enum item_type ntype; + unsigned group; + char *descr; + struct gar2navit *next; +}; + +#define G2N_POINT 1 +#define G2N_POLYLINE 2 +#define G2N_POLYGONE 3 +#define G2N_KIND_MASK 0xF0000000 +#define G2N_KIND_SHIFT 28 +#define G2N_RGN1 (GROUP_RGN1<<29) +#define G2N_RGN2 (GROUP_RGN2<<29) + +struct gar2nav_conv { + struct gar2navit *points; + struct gar2navit *polylines; + struct gar2navit *polygons; +}; + +struct gar2nav_conv *g2n_conv_load(char *file); +enum item_type g2n_get_type(struct gar2nav_conv *c, unsigned int type, unsigned short id); +char *g2n_get_descr(struct gar2nav_conv *c, int type, unsigned short id); +struct gar2nav_conv *g2n_default_conv(void); diff --git a/map/garmin/garmin.c b/map/garmin/garmin.c new file mode 100644 index 00000000..da0f5804 --- /dev/null +++ b/map/garmin/garmin.c @@ -0,0 +1,997 @@ +/* + Copyright (C) 2007 Alexander Atanasov <aatanasov@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 + + Garmin and MapSource are registered trademarks or trademarks + of Garmin Ltd. or one of its subsidiaries. + +*/ + +#include <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include "config.h" +#include "plugin.h" +#include "data.h" +#include "projection.h" +#include "item.h" +#include "debug.h" +#include "map.h" +#include "maptype.h" +#include "attr.h" +#include "coord.h" +#include "transform.h" +#include <stdio.h> +#include "attr.h" +#include "coord.h" +#include <libgarmin.h> +#include "garmin.h" +#include "gar2navit.h" + + +static int map_id; + +struct map_priv { + int id; + char *filename; + struct gar2nav_conv *conv; + struct gar *g; +}; + +struct map_rect_priv { + int id; + struct coord_rect r; + char *label; // FIXME: Register all strings for searches + int limit; + struct map_priv *mpriv; + struct gmap *gmap; + struct gobject *cobj; + struct gobject *objs; + struct item item; + unsigned int last_coord; + void *last_itterated; + struct coord last_c; + void *last_oattr; + unsigned int last_attr; + struct gar_search *search; +}; + +int garmin_debug = 10; + +void +logfn(char *file, int line, int level, char *fmt, ...) +{ + va_list ap; + char fileline[256]; + int sz; + if (level > garmin_debug) + return; + va_start(ap, fmt); + sz = sprintf(fileline, "%s:%d:%d|", file, line, level); + debug_vprintf(0, "", strlen(""), fileline, sz, + 1, fmt, ap); + va_end(ap); +} +// need a base map and a map +struct gscale { + char *label; + float scale; + int bits; +}; + +static struct gscale mapscales[] = { + {"7000 km", 70000.0, 8} + ,{"5000 km", 50000.0, 8} + ,{"3000 km", 30000.0, 9} + ,{"2000 km", 20000.0, 9} + ,{"1500 km", 15000.0, 10} + ,{"1000 km", 10000.0, 10} + ,{"700 km", 7000.0, 11} + ,{"500 km", 5000.0, 11} + ,{"300 km", 3000.0, 13} + ,{"200 km", 2000.0, 13} + ,{"150 km", 1500.0, 13} + ,{"100 km", 1000.0, 14} + ,{"70 km", 700.0, 15} + ,{"50 km", 500.0, 16} + ,{"30 km", 300.0, 16} + ,{"20 km", 200.0, 17} + ,{"15 km", 150.0, 17} + ,{"10 km", 100.0, 18} + ,{"7 km", 70.0, 18} + ,{"5 km", 50.0, 19} + ,{"3 km", 30.0, 19} + ,{"2 km", 20.0, 20} + ,{"1.5 km", 15.0, 22} + ,{"1 km", 10.0, 24} + ,{"700 m", 7.0, 24} + ,{"500 m", 5.0, 24} + ,{"300 m", 3.0, 24} + ,{"200 m", 2.0, 24} + ,{"150 m", 1.5, 24} + ,{"100 m", 1.0, 24} + ,{"70 m", 0.7, 24} + ,{"50 m", 0.5, 24} + ,{"30 m", 0.3, 24} + ,{"20 m", 0.2, 24} + ,{"15 m", 0.1, 24} + ,{"10 m", 0.15, 24} +}; + + +static int +garmin_object_label(struct gobject *o, struct attr *attr) +{ + struct map_rect_priv *mr = o->priv_data; + char *codepage; + char *label; + if (!mr) { + dlog(1, "Error object do not have priv_data!!\n"); + return 0; + } + if (mr->label) { + free(mr->label); + } + label = gar_get_object_lbl(o); + if (label) { + codepage = gar_obj_codepage(o); + if (*codepage != 'a') { + mr->label = g_convert(label, -1,"utf-8",codepage,NULL,NULL,NULL); + free(label); + } else + mr->label = label; + } else { + mr->label = NULL; + return 0; + } + if (mr->label) { + char *cp = mr->label; +#warning FIXME Process label and give only the visible part + if (*mr->label == '@' || *mr->label == '^') + cp++; + /* FIXME: If zoomlevel is high convert ^ in the string to spaces */ + attr->u.str = cp; + return 1; + } + return 0; +} + +static int +garmin_object_debug(struct gobject *o, struct attr *attr) +{ + struct map_rect_priv *mr = o->priv_data; + if (!mr) { + dlog(1, "Error object do not have priv_data!!\n"); + return 0; + } + if (mr->label) + free(mr->label); + mr->label = gar_object_debug_str(o); + if (mr->label) { + attr->u.str = mr->label; + return 1; + } + return 0; +} + + +static struct map_search_priv * +gmap_search_new(struct map_priv *map, struct item *item, struct attr *search, int partial) +{ + struct map_rect_priv *mr=g_new0(struct map_rect_priv, 1); + struct gar_search *gs; + int rc; + + dlog(1, "Called!\n"); + mr->mpriv=map; + gs = g_new0(struct gar_search,1); + if (!gs) { + dlog(1, "Can not init search \n"); + free(mr); + return NULL; + } + mr->search = gs; + switch (search->type) { + case attr_country_name: + gs->type = GS_COUNTRY; + break; + case attr_town_name: + gs->type = GS_CITY; + break; + case attr_town_postal: + gs->type = GS_ZIP; + break; + case attr_street_name: + gs->type = GS_ROAD; + break; +#if someday + case attr_region_name: + case attr_intersection: + case attr_housenumber: +#endif + default: + dlog(1, "Don't know how to search for %d\n", search->type); + goto out_err; + } + gs->match = partial ? GM_START : GM_EXACT; + gs->needle = strdup(search->u.str); + dlog(5, "Needle: %s\n", gs->needle); + + mr->gmap = gar_find_subfiles(mr->mpriv->g, gs, GO_GET_SEARCH); + if (!mr->gmap) { + dlog(1, "Can not init search \n"); + goto out_err; + } + rc = gar_get_objects(mr->gmap, 0, gs, &mr->objs, GO_GET_SEARCH); + if (rc < 0) { + dlog(1, "Error loading objects\n"); + goto out_err; + } + mr->cobj = mr->objs; + dlog(4, "Loaded %d objects\n", rc); + return (struct map_search_priv *)mr; + +out_err: + free(gs); + free(mr); + return NULL; +} + +/* Assumes that only one item will be itterated at time! */ +static void +coord_rewind(void *priv_data) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + mr->last_coord = 0; +}; + +static void +attr_rewind(void *priv_data) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + mr->last_attr = 0; +}; + +static int +point_coord_get(void *priv_data, struct coord *c, int count) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + struct gcoord gc; + if (!count) + return 0; + if (g != mr->last_itterated) { + mr->last_itterated = g; + mr->last_coord = 0; + } + + if (mr->last_coord > 0) + return 0; + + gar_get_object_coord(mr->gmap, g, &gc); + c->x = gc.x; + c->y = gc.y; + mr->last_coord++; +// dlog(1,"point: x=%d y=%d\n", c->x, c->y); + // dlog(1, "point: x=%f y=%f\n", GARDEG(c->x), GARDEG(c->y)); + return 1; +} + +static int +coord_is_node(void *priv_data) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + + return gar_is_object_dcoord_node(mr->gmap, g, mr->last_coord); +} + +static int +poly_coord_get(void *priv_data, struct coord *c, int count) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + int ndeltas = 0, total = 0; + struct gcoord dc; + + if (!count) + return 0; + + if (g != mr->last_itterated) { + mr->last_itterated = g; + mr->last_coord = 0; + } + ndeltas = gar_get_object_deltas(g); + if (mr->last_coord > ndeltas + 1) + return 0; + while (count --) { + if (mr->last_coord == 0) { + gar_get_object_coord(mr->gmap, g, &dc); + mr->last_c.x = dc.x; + mr->last_c.y = dc.y; + } else { + if (!gar_get_object_dcoord(mr->gmap, g, mr->last_coord - 1, &dc)) { + mr->last_coord = ndeltas + 2; + return total; + } + mr->last_c.x += dc.x; + mr->last_c.y += dc.y; + } + c->x = mr->last_c.x; + c->y = mr->last_c.y; + ddlog(1, "poly: x=%f y=%f\n", GARDEG(c->x), GARDEG(c->y)); +// dlog(1,"poly: x=%d y=%d\n", c->x, c->y); + c++; + total++; + mr->last_coord ++; + } + return total; +} + +// for _any we must return one by one +static int +point_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + int rc; + switch (attr_type) { + case attr_any: + if (g != mr->last_oattr) { + mr->last_oattr = g; + mr->last_attr = 0; + } + switch(mr->last_attr) { + case 0: + mr->last_attr++; + attr->type = attr_label; + rc = garmin_object_label(g, attr); + if (rc) + return rc; + case 1: + mr->last_attr++; + attr->type = attr_debug; + rc = garmin_object_debug(g, attr); + if (rc) + return rc; + case 2: + mr->last_attr++; + if (g->type == GO_POLYLINE) { + attr->type = attr_street_name; + rc = garmin_object_label(g, attr); + if (rc) + return rc; + } + case 3: + mr->last_attr++; + attr->type = attr_flags; + attr->u.num = 0; + rc = gar_object_flags(g); + if (rc & F_ONEWAY) + attr->u.num |= AF_ONEWAY; + if (rc & F_SEGMENTED) + attr->u.num |= AF_SEGMENTED; + return 1; + default: + return 0; + } + break; + case attr_label: + attr->type = attr_label; + return garmin_object_label(g, attr); + case attr_town_name: + attr->type = attr_town_name; + return garmin_object_label(g, attr); + case attr_street_name: + attr->type = attr_type; + return garmin_object_label(g, attr); + case attr_street_name_systematic: + /* TODO: Get secondary labels of roads */ + return 0; + case attr_flags: + attr->type = attr_flags; + attr->u.num = 0; + rc = gar_object_flags(g); + if (rc & F_ONEWAY) + attr->u.num |= AF_ONEWAY; + if (rc & F_SEGMENTED) + attr->u.num |= AF_SEGMENTED; + return 1; + default: + dlog(1, "Dont know about attribute %d[%04X]=%s yet\n", attr_type,attr_type, attr_to_name(attr_type)); + } + + return 0; +} + +static struct item_methods methods_garmin_point = { + coord_rewind, + point_coord_get, + attr_rewind, + point_attr_get, +}; + +static struct item_methods methods_garmin_poly = { + coord_rewind, + poly_coord_get, + attr_rewind, // point_attr_rewind, + point_attr_get, // poly_attr_get, + coord_is_node, +}; + +static int +search_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + int rc; + switch (attr_type) { + case attr_any: + if (g != mr->last_oattr) { + mr->last_oattr = g; + mr->last_attr = 0; + } + switch(mr->last_attr) { + case 0: + mr->last_attr++; + attr->type = attr_label; + rc = garmin_object_label(g, attr); + if (rc) + return rc; + case 1: + mr->last_attr++; + attr->type = attr_debug; + rc = garmin_object_debug(g, attr); + if (rc) + return rc; + case 2: + mr->last_attr++; + if (g->type == GO_POLYLINE) { + attr->type = attr_street_name; + rc = garmin_object_label(g, attr); + if (rc) + return rc; + } + case 3: + mr->last_attr++; + attr->type = attr_flags; + attr->u.num = 0; + rc = gar_object_flags(g); + if (rc & F_ONEWAY) + attr->u.num |= AF_ONEWAY; + if (rc & F_SEGMENTED) + attr->u.num |= AF_SEGMENTED; + return 1; + default: + return 0; + } + break; + case attr_label: + attr->type = attr_label; + return garmin_object_label(g, attr); + case attr_town_name: + attr->type = attr_town_name; + if (mr->label) + free(mr->label); + mr->label = gar_srch_get_city(g); + attr->u.str = mr->label; + if (attr->u.str) + return 1; + return 0; + case attr_town_id: + rc = gar_srch_get_cityid(g); + if (rc) { + attr->type = attr_town_id; + attr->u.num = rc; + return 1; + } + return 0; + case attr_town_postal: + attr->type = attr_town_postal; + attr->u.str = gar_srch_get_zip(g); + if (attr->u.str) + return 1; + return 0; + case attr_street_name: + attr->type = attr_street_name; + if (mr->label) + free(mr->label); + mr->label = gar_srch_get_roadname(g); + attr->u.str = mr->label; + if (attr->u.str) + return 1; + return 0; + case attr_street_id: + attr->type = attr_street_id; + attr->u.num = gar_srch_get_roadid(g); + if (attr->u.num) + return 1; + return 0; + case attr_flags: + attr->type = attr_flags; + attr->u.num = 0; + rc = gar_object_flags(g); + if (rc & F_ONEWAY) + attr->u.num |= AF_ONEWAY; + if (rc & F_SEGMENTED) + attr->u.num |= AF_SEGMENTED; + return 1; + case attr_country_id: + rc = gar_srch_get_countryid(g); + if (rc) { + attr->type = attr_country_id; + attr->u.num = rc; + return 1; + } + return 0; + case attr_country_name: + attr->type = attr_country_name; + attr->u.str = gar_srch_get_country(g); + if (attr->u.str) + return 1; + return 0; + case attr_district_id: + rc = gar_srch_get_regionid(g); + if (rc) { + attr->type = attr_district_id; + attr->u.num = rc; + return 1; + } + return 0; + case attr_district_name: + attr->type = attr_district_name; + attr->u.str = gar_srch_get_region(g); + if (attr->u.str) + return 1; + return 0; + case attr_town_streets_item: + return 0; + default: + dlog(1, "Dont know about attribute %d[%04X]=%s yet\n", + attr_type,attr_type, attr_to_name(attr_type)); + } + + return 0; +} + +static int +search_coord_get(void *priv_data, struct coord *c, int count) +{ + struct gobject *g = priv_data; + struct map_rect_priv *mr = g->priv_data; + struct gcoord gc; + if (!count) + return 0; + if (g != mr->last_itterated) { + mr->last_itterated = g; + mr->last_coord = 0; + } + + if (mr->last_coord > 0) + return 0; + + if (gar_get_object_coord(mr->gmap, g, &gc)) { + c->x = gc.x; + c->y = gc.y; + mr->last_coord++; + return 1; + } + return 0; +} + +static struct item_methods methods_garmin_search = { + coord_rewind, + search_coord_get, + attr_rewind, + search_attr_get, +}; + + +static struct item * +garmin_poi2item(struct map_rect_priv *mr, struct gobject *o, unsigned short otype) +{ + if (mr->mpriv->conv) { + int mask = gar_object_group(o) << G2N_KIND_SHIFT; + mr->item.type = g2n_get_type(mr->mpriv->conv, G2N_POINT|mask, otype); + } + mr->item.meth = &methods_garmin_point; + return &mr->item; +} + +static struct item * +garmin_pl2item(struct map_rect_priv *mr, struct gobject *o, unsigned short otype) +{ + if (mr->mpriv->conv) { + int mask = gar_object_group(o) << G2N_KIND_SHIFT; + mr->item.type = g2n_get_type(mr->mpriv->conv, G2N_POLYLINE|mask, otype); + } + mr->item.meth = &methods_garmin_poly; + return &mr->item; +} + +static struct item * +garmin_pg2item(struct map_rect_priv *mr, struct gobject *o, unsigned short otype) +{ + if (mr->mpriv->conv) { + int mask = gar_object_group(o) << G2N_KIND_SHIFT; + mr->item.type = g2n_get_type(mr->mpriv->conv, G2N_POLYGONE|mask, otype); + } + mr->item.meth = &methods_garmin_poly; + return &mr->item; +} + +static struct item * +garmin_srch2item(struct map_rect_priv *mr, struct gobject *o, unsigned short otype) +{ + mr->item.type = type_country_label; + mr->item.meth = &methods_garmin_search; + return &mr->item; +} + +static struct item * +garmin_obj2item(struct map_rect_priv *mr, struct gobject *o) +{ + unsigned short otype; + otype = gar_obj_type(o); + mr->item.type = type_none; + switch (o->type) { + case GO_POINT: + return garmin_poi2item(mr, o, otype); + case GO_POLYLINE: + return garmin_pl2item(mr, o, otype); + case GO_POLYGON: + return garmin_pg2item(mr, o, otype); + case GO_ROAD: + return garmin_pl2item(mr, o, otype); +#if 0 + case GO_SEARCH: + return garmin_srch2item(mr, o, otype); +#endif + default: + dlog(1, "Unknown garmin object type:%d\n", + o->type); + } + return NULL; +} + +static struct item * +gmap_rect_get_item_byid(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + struct gobject *o; + o = mr->objs = gar_get_object_by_id(mr->mpriv->g, id_hi, id_lo); + if (!o) { + dlog(1, "Can not find object\n"); + return NULL; + } + + mr->item.id_hi = id_hi; + mr->item.id_lo = id_lo; + mr->item.priv_data = o; + mr->item.type = type_none; + o->priv_data = mr; + if (!garmin_obj2item(mr, o)) + return NULL; + return &mr->item; +} + +static struct item * +gmap_rect_get_item(struct map_rect_priv *mr) +{ + struct gobject *o; + if (!mr->objs) + return NULL; + if (!mr->cobj) + return NULL; + // mr->cobj = mr->objs; + o = mr->cobj; +// dlog(1, "gi:o=%p\n", o); + mr->cobj = mr->cobj->next; + if (o) { + mr->item.id_hi = gar_object_mapid(o); + mr->item.id_lo = gar_object_index(o); + mr->item.priv_data = o; + mr->item.type = type_none; + o->priv_data = mr; + if (!garmin_obj2item(mr, o)) + return NULL; + return &mr->item; + } + return NULL; +} + +#define max(a,b) ((a) > (b) ? (a) : (b)) +struct nl2gl_t { + int g; + int bits; + char *descr; +}; + +struct nl2gl_t nl2gl_1[] = { + { /* 0 */ .g = 12, .descr = "0-120m", }, + { /* 1 */ .g = 11, .descr = "0-120m", }, + { /* 2 */ .g = 10, .descr = "0-120m", }, + { /* 3 */ .g = 9, .descr = "0-120m", }, + { /* 4 */ .g = 8, .descr = "0-120m", }, + { /* 5 */ .g = 7, .descr = "0-120m", }, + { /* 6 */ .g = 6, .descr = "0-120m", }, + { /* 7 */ .g = 5, .descr = "0-120m", }, + { /* 8 */ .g = 4, .descr = "0-120m", }, + { /* 9 */ .g = 4, .descr = "0-120m", }, + { /* 10 */ .g = 3, .descr = "0-120m", }, + { /* 11 */ .g = 3, .descr = "0-120m", }, + { /* 12 */ .g = 2, .descr = "0-120m", }, + { /* 13 */ .g = 2, .descr = "0-120m", }, + { /* 14 */ .g = 2, .descr = "0-120m", }, + { /* 15 */ .g = 1, .descr = "0-120m", }, + { /* 16 */ .g = 1, .descr = "0-120m", }, + { /* 17 */ .g = 1, .descr = "0-120m", }, + { /* 18 */ .g = 0, .descr = "0-120m", }, +}; + +struct nl2gl_t nl2gl[] = { + { /* 0 */ .g = 9, .descr = "0-120m", }, + { /* 1 */ .g = 9, .descr = "0-120m", }, + { /* 2 */ .g = 8, .descr = "0-120m", }, + { /* 3 */ .g = 8, .descr = "0-120m", }, + { /* 4 */ .g = 7, .descr = "0-120m", }, + { /* 5 */ .g = 7, .descr = "0-120m", }, + { /* 6 */ .g = 6, .descr = "0-120m", }, + { /* 7 */ .g = 6, .descr = "0-120m", }, + { /* 8 */ .g = 5, .descr = "0-120m", }, + { /* 9 */ .g = 5, .descr = "0-120m", }, + { /* 10 */ .g = 4, .descr = "0-120m", }, + { /* 11 */ .g = 4, .descr = "0-120m", }, + { /* 12 */ .g = 3, .descr = "0-120m", }, + { /* 13 */ .g = 3, .descr = "0-120m", }, + { /* 14 */ .g = 2, .descr = "0-120m", }, + { /* 15 */ .g = 2, .descr = "0-120m", }, + { /* 16 */ .g = 1, .descr = "0-120m", }, + { /* 17 */ .g = 1, .descr = "0-120m", }, + { /* 18 */ .g = 0, .descr = "0-120m", }, +}; + +static int +get_level(struct map_selection *sel) +{ + return sel->order; +} + +static int +garmin_get_selection(struct map_rect_priv *map, struct map_selection *sel) +{ + struct gar_rect r; + struct gmap *gm; + struct gobject **glast = NULL; + int rc; + int sl, el; + int level = 0; // 18; /* max level for maps, overview maps can have bigger + /* levels we do not deal w/ them + */ + int flags = 0; + if (sel && sel->range.min == type_street_0 && sel->range.max == type_ferry) { + // Get all roads + flags = GO_GET_ROUTABLE; + } else if (sel) + flags = GO_GET_SORTED; + + if (sel) { + r.lulat = sel->u.c_rect.lu.y; + r.lulong = sel->u.c_rect.lu.x; + r.rllat = sel->u.c_rect.rl.y; + r.rllong = sel->u.c_rect.rl.x; + level = get_level(sel); +// level = nl2gl[level].g; + dlog(2, "Looking level=%d for %f %f %f %f\n", + level, r.lulat, r.lulong, r.rllat, r.rllong); + } + gm = gar_find_subfiles(map->mpriv->g, sel ? &r : NULL, flags); + if (!gm) { + if (sel) { + dlog(1, "Can not find map data for the area: %f %f %f %f\n", + r.lulat, r.lulong, r.rllat, r.rllong); + } else { + dlog(1, "Can not find map data\n"); + } + return -1; + } +#if 0 + sl = (18-(gm->maxlevel - gm->minlevel))/2; + el = sl + (gm->maxlevel - gm->minlevel); + if (level < sl) + level = sl; + if (level > el) + level = el; + level = level - sl; + level = (gm->maxlevel - gm->minlevel) - level; + dlog(3, "sl=%d el=%d level=%d\n", sl, el, level); +#endif + sl = (18-gm->zoomlevels)/2; + el = sl + gm->zoomlevels; + if (level < sl) + level = sl; + if (level > el) + level = el; + level = level - sl; + level = gm->basebits + level; + dlog(3, "sl=%d el=%d level=%d\n", sl, el, level); + map->gmap = gm; + glast = &map->objs; + while (*glast) { + if ((*glast)->next) { + *glast = (*glast)->next; + } else + break; + } + rc = gar_get_objects(gm, level, sel ? &r : NULL, glast, flags); + if (rc < 0) { + dlog(1, "Error loading objects\n"); + return -1; + } + map->cobj = map->objs; + dlog(2, "Loaded %d objects\n", rc); + return rc; +} +// Can not return NULL, navit segfaults +static struct map_rect_priv * +gmap_rect_new(struct map_priv *map, struct map_selection *sel) +{ + struct map_selection *ms = sel; + struct map_rect_priv *mr; + + if (!map) + return NULL; + mr = calloc(1, sizeof(*mr)); + if (!mr) + return mr; + mr->mpriv = map; + if (!sel) { + return mr; + } else { + while (ms) { + dlog(2, "order %d\n", ms->order); + if (garmin_get_selection(mr, ms) < 0) { + // free(mr); + // return NULL; + } + ms = ms->next; + } + } + return mr; +} + +static void +gmap_rect_destroy(struct map_rect_priv *mr) +{ + dlog(11,"destroy maprect\n"); + if (mr->gmap) + gar_free_gmap(mr->gmap); + if (mr->objs) + gar_free_objects(mr->objs); + if (mr->label) + free(mr->label); + free(mr); +} + +static void +gmap_search_destroy(struct map_search_priv *ms) +{ + gmap_rect_destroy((struct map_rect_priv *)ms); +} + +static void +gmap_destroy(struct map_priv *m) +{ + dlog(5, "garmin_map_destroy\n"); + if (m->g) + gar_free(m->g); + if (m->filename) + free(m->filename); + free(m); +} + +static struct map_methods map_methods = { + projection_garmin, + "utf-8", + gmap_destroy, + gmap_rect_new, + gmap_rect_destroy, + gmap_rect_get_item, + gmap_rect_get_item_byid, + gmap_search_new, + gmap_search_destroy, + gmap_rect_get_item, +}; + +static struct map_priv * +gmap_new(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + struct attr *data; + struct attr *debug; + struct attr *flags; + char buf[PATH_MAX]; + struct stat st; + int dl = 1; + struct gar_config cfg; + int debugmask = 0; + + data=attr_search(attrs, NULL, attr_data); + if (! data) + return NULL; + debug=attr_search(attrs, NULL, attr_debug); + if (debug) { + dl = atoi(debug->u.str); + if (!dl) + dl = 1; + } + flags=attr_search(attrs, NULL, attr_flags); + if (flags) { + debugmask = flags->u.num; + } + m=g_new(struct map_priv, 1); + m->id=++map_id; + m->filename = strdup(data->u.str); + if (!m->filename) { + g_free(m); + return NULL; + } + memset(&cfg, 0, sizeof(struct gar_config)); + cfg.opm = OPM_GPS; + cfg.debuglevel = dl; + cfg.debugmask = debugmask; + garmin_debug = dl; + m->g = gar_init_cfg(NULL, logfn, &cfg); + if (!m->g) { + g_free(m->filename); + g_free(m); + return NULL; + } + // we want the data now, later we can load only what's necessery + if (gar_img_load(m->g, m->filename, 1) < 0) { + gar_free(m->g); + g_free(m->filename); + g_free(m); + return NULL; + } + m->conv = NULL; + snprintf(buf, sizeof(buf), "%s.types", m->filename); + if (!stat(buf, &st)) { + dlog(1, "Loading custom types from %s\n", buf); + m->conv = g2n_conv_load(buf); + } + if (!m->conv) { + dlog(1, "Using builtin types\n"); + m->conv = g2n_default_conv(); + } + if (!m->conv) { + dlog(1, "Failed to load map types\n"); + } + *meth=map_methods; + return m; +} + +void +plugin_init(void) +{ + plugin_register_map_type("garmin", gmap_new); +} diff --git a/map/garmin/garmin.h b/map/garmin/garmin.h new file mode 100644 index 00000000..94e893b1 --- /dev/null +++ b/map/garmin/garmin.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2007 Alexander Atanasov <aatanasov@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 + + Garmin and MapSource are registered trademarks or trademarks + of Garmin Ltd. or one of its subsidiaries. + +*/ + +#define dlog(x,y ...) logfn(__FILE__,__LINE__,x, ##y) +#ifdef HARDDEBUG +#define ddlog(x,y ...) logfn(__FILE__,__LINE__,x, ##y) +#else +#define ddlog(x,y ...) +#endif + +extern int garmin_debug; +void logfn(char *file, int line, int level, char *fmt, ...); + diff --git a/map/garmin/garmintypes.txt b/map/garmin/garmintypes.txt new file mode 100644 index 00000000..7df6831d --- /dev/null +++ b/map/garmin/garmintypes.txt @@ -0,0 +1,707 @@ +# +# +# +POINT +0, 0x0000 = label_unkn, Unknown label +0, 0x0100 = town_label_1e5, Megapolis (10M +) +0, 0x0200 = town_label_5e4, Megapolis (5-10M) +0, 0x0300 = town_label_2e4, Big City (2-5M) +0, 0x0400 = town_label_1e4, Big City (1-2M) +0, 0x0500 = town_label_5e3, Big City (0.5-1M) +0, 0x0600 = town_label_2e3, City (200-500k) +0, 0x0700 = town_label_1e3, City (100-200k) +0, 0x0800 = town_label_5e2, City (50-100k) +0, 0x0900 = town_label_2e2, City (20-50k) +0, 0x0a00 = town_label_1e2, City (10-20k) +0, 0x0b00 = town_label_5e1, Small City (5-10k) +0, 0x0c00 = town_label_2e1, Small City (2-5k) +0, 0x0d00 = town_label_1e1, Village (1-2k) +0, 0x0e00 = town_label_5e0, Village (500-1000) +0, 0x0f00 = town_label_2e0, Village (200-500) +0, 0x1000 = town_label_1e0, Village (100-200) +0, 0x1100 = town_label_0e0, Village (0-100) + +0, 0x1200 = port_label, Port with services +0, 0x1300 = label_unkn, Unknown label2 +0, 0x1400-0x14FF = country_label, Large Country Name +0, 0x1500-0x15FF = country_label, Country Name +0, 0x1c00 = poi_wreck, Unclassified Obstruction +0, 0x1c01 = poi_wreck, Wreck +0, 0x1c02 = poi_dangerous, submerged wreck, dangerous +0, 0x1c03 = poi_nondangerous, submerged wreck, non-dangerous +0, 0x1c04 = poi_wreck, Wreck, cleared by wire drag +0, 0x1c05 = poi_rock, Obstruction, visible at high water +0, 0x1c06 = poi_rock, Obstruction, awash +0, 0x1c07 = poi_rock, Obstruction, submerged +0, 0x1c08 = poi_rock, Obstruction, cleared by wire drag +0, 0x1c09 = poi_rock, Rock, awash +0, 0x1c0a = poi_rock, Rock, submerged at low Water +0, 0x1c0b = poi_sounding, Sounding +0, 0x1d00 = poi_tide, Tide Prediction + +0, 0x1e00-0x1e3f = district_label, District, Province, State Name +0, 0x1f00 = district_label_0e0, Region, District Name + +# Fixme if it has label how to change to highway_exit_with_label ?? +0, 0x2000-0x203F = highway_exit, Exit +0, 0x2100-0x213F = highway_exit, Exit with Services +0, 0x2200-0x223F = highway_exit, Exit with Restroom +0, 0x2300-0x233F = highway_exit, Exit with Convinience Store +0, 0x2400-0x243F = highway_exit, Exit with Weight Station +0, 0x2500-0x253F = highway_exit, Exit with Toolbooth Booth +0, 0x2600-0x263F = highway_exit, Exit with Information +0, 0x2700-0x273F = highway_exit, Exit + +0, 0x2800-0x283F = district_label_1e0, Region Name + +0, 0x2900 = poi_public_utilities, Services + +0, 0x2A00 = poi_dining, Dining(Other) +0, 0x2A01 = poi_dining, Dining(American) +0, 0x2A02 = poi_dining, Dining(Asian) +0, 0x2A03 = poi_dining, Dining(Barbecue) +0, 0x2A04 = poi_dining, Dining(Chinese) +0, 0x2A05 = poi_dining, Dining(Deli/Bakery) +0, 0x2A06 = poi_dining, Dining(International) +0, 0x2A07 = poi_fastfood, Fast Food +0, 0x2A08 = poi_dining, Dining(Italian) +0, 0x2A09 = poi_dining, Dining(Mexican) +0, 0x2A0A = poi_dining, Dining(Pizza) +0, 0x2A0B = poi_dining, Dining(Sea Food) +0, 0x2A0C = poi_dining, Dining(Steak/Grill) +0, 0x2A0D = poi_dining, Dining(Bagel/Donut) +0, 0x2A0E = poi_dining, Dining(Cafe/Diner) +0, 0x2A0F = poi_dining, Dining(French) +0, 0x2A10 = poi_dining, Dining(German) +0, 0x2A11 = poi_dining, Dining(British Isles) +0, 0x2A12 = poi_dining, Dining(Special Foods) + +0, 0x2B00 = poi_hotel, Hotel(Other) +0, 0x2B01 = poi_hotel, Hotel/Motel +0, 0x2B02 = poi_hotel, Bed & Breakfast inn +0, 0x2B03 = poi_camp_rv, Camping/RV-Park +0, 0x2B04 = poi_resort, Resort + +0, 0x2C00 = poi_attraction, Amusement Park +0, 0x2C01 = poi_attraction, Amusement Park +0, 0x2C02 = poi_museum_history, Museum/History +0, 0x2C03 = poi_library, Libraries +0, 0x2C04 = poi_landmark, Land Mark +0, 0x2C05 = poi_school, School +0, 0x2C06 = poi_park, Park +0, 0x2C07 = poi_zoo, Zoo +0, 0x2C08 = poi_stadium, Sportpark, Stadium,(point) +0, 0x2C09 = poi_fair, Fair, Conference(point) +0, 0x2C0A = poi_wine, Wine restaurant(point) +0, 0x2C0B = poi_worship, Place of Worship +0, 0x2C0C = poi_hotspring, Hot Spring + +0, 0x2D00 = poi_theater, Theater +0, 0x2D01 = poi_theater, Theater +0, 0x2D02 = poi_bar, Bar +0, 0x2D03 = poi_cinema, Cinema +0, 0x2D04 = poi_casino, Casino +0, 0x2D05 = poi_golf, Golf +0, 0x2D06 = poi_skiing, Skiing Center +0, 0x2D07 = poi_bowling, Bowling +0, 0x2D08 = poi_icesport, Ice/Sporting +0, 0x2D09 = poi_swimming, Swimming +0, 0x2D0A = poi_sport, Sports(point) +0, 0x2D0B = poi_sailing, Sailing Airport + +0, 0x2E00 = poi_shopping, Shoping general +0, 0x2E01 = poi_shop_department, Department Store +0, 0x2E02 = poi_shop_grocery, Grocery +0, 0x2E03 = poi_shop_merchandise, General Merchandiser +0, 0x2E04 = poi_mall, Shopping Center +0, 0x2E05 = poi_pharmacy, Pharmacy +0, 0x2E06 = poi_shopping, Convenience +0, 0x2E07 = poi_shop_apparel, Apparel +0, 0x2E08 = poi_shop_handg, House and Garden +0, 0x2E09 = poi_shop_furnish, Home Furnishing +0, 0x2E0a = poi_shop_retail, Special Retail +0, 0x2E0b = poi_shop_computer, Computer/Software +0, 0x2E0c = poi_shop_computer, Mobile Communications + +0, 0x2F00 = poi_service, generic service +0, 0x2F01 = poi_fuel, Fuel/Gas +0, 0x2F02 = poi_car_rent, Car Rental +0, 0x2F03 = poi_autoservice, Car Repair +0, 0x2F04 = poi_airport, Airport +0, 0x2F05 = poi_post, Post Office +0, 0x2F06 = poi_bank, Bank +0, 0x2F07 = poi_car_dealer_parts, Car Dealer(point) +0, 0x2F08 = poi_bus_station, Bus Station +0, 0x2F09 = poi_marina, Marina +0, 0x2F0A = poi_wrecker, Wrecker Service +0, 0x2F0B = poi_car_parking, Parking +0, 0x2F0C = poi_rest_room, Restroom +0, 0x2F0D = poi_auto_club, Automobile Club +0, 0x2F0E = poi_car_wash, Car Wash +0, 0x2F0F = poi_garmin, Garmin Dealer +0, 0x2F10 = poi_personal_service, Personal Service +0, 0x2F11 = poi_bussines_service, Business Service +0, 0x2F12 = poi_communication, Communication +0, 0x2F13 = poi_repair_service, Repair Service +0, 0x2F14 = poi_social_service, Social Service +0, 0x2F15 = poi_public_utilities, Utility +0, 0x2F16 = poi_truck_stop, Truck Stop +0, 0x2F17 = poi_bus_stop, Bus Stop + +0, 0x3000 = poi_emergency, generic emergency/government +0, 0x3001 = poi_police, Police Station +0, 0x3002 = poi_hospital, Hospital +0, 0x3003 = poi_public_office, Public Office +0, 0x3004 = poi_justice, Justice +0, 0x3005 = poi_concert, Concert hall(point) +0, 0x3006 = poi_border_station, Border Station(point) +0, 0x3007 = poi_goverment_building, Goverment Building +0, 0x3008 = poi_firebrigade, FireFighters Station + +0, 0x4000-0x403F = poi_golf, Golf +0, 0x4100-0x413F = poi_fish, Fish +0, 0x4200-0x423F = poi_wreck, Wreck +0, 0x4300-0x433F = poi_marina, Marina +0, 0x4400-0x443F = poi_fuel, Gas +0, 0x4500-0x453F = poi_restaurant, Restaurant +0, 0x4600-0x463F = poi_bar, Bar +0, 0x4700-0x473F = poi_boat_ramp, Boat Ramp +0, 0x4800-0x483F = poi_camping, Camping +0, 0x4900-0x493F = poi_park, Park +0, 0x4A00-0x4A3F = poi_picnic, Picnic Area +0, 0x4B00-0x4B3F = poi_hospital, Hospital +0, 0x4C00-0x4C3F = poi_information, Information +0, 0x4D00-0x4D3F = poi_car_parking, Parking +0, 0x4E00-0x4E3F = poi_restroom, Restroom +0, 0x4F00-0x4F3F = poi_shower, Shower +0, 0x5000-0x503F = poi_drinking_water, Drinking Water +0, 0x5100-0x513F = poi_telephone, Telephone +0, 0x5200-0x523F = poi_scenic_area, Scenic Area +0, 0x5300-0x533F = poi_skiing, Skiing +0, 0x5400-0x543F = poi_swimming, Swimming +0, 0x5500-0x553F = poi_dam, Dam + +0, 0x5600-0x563F = poi_forbiden_area, Forbiden Area +0, 0x5700-0x573F = poi_danger_area, Danger Area +0, 0x5800-0x583F = poi_restricted_area, Restricted Area + +0, 0x5900 = poi_airport, Generic Airport +0, 0x5901 = poi_airport, Large Airport +0, 0x5902 = poi_airport, Medium Airport +0, 0x5903 = poi_airport, Small Airport +0, 0x5904 = poi_heliport, Heliport +0, 0x5905-0x593F = poi_airport, Airport + +0, 0x5a00 = poi_mark, Kilometer Pole +0, 0x5b00 = poi_mark, Kolokol +0, 0x5c00 = poi_diving, Diving Place + +0, 0x5D00-0x5D3F = poi_daymark, Daymark,Green Square +0, 0x5E00-0x5E3F = poi_daymark, Daymark,Red Triangle + +0, 0x6000 = poi_loudspeaker, LoudSpeaker +0, 0x6100 = poi_building, House + +0, 0x6200 = poi_height, Height with point in feet one decimal place +0, 0x6300 = poi_height, Height without point in feet no decimal place +0, 0x6400 = poi_manmade_feature, Manmade Feature +0, 0x6401 = poi_bridge, Bridge +0, 0x6402 = poi_building, Building +0, 0x6403 = poi_cemetery, Cemetery +0, 0x6404 = poi_church, Church +0, 0x6405 = poi_civil, Civil +0, 0x6406 = poi_crossing, Crossing +0, 0x6407 = poi_dam, Dam +0, 0x6408 = poi_hospital, Hospital +0, 0x6409 = poi_levee, Levee +0, 0x640A = poi_locale, Locale +0, 0x640B = poi_military, Military +0, 0x640C = poi_mine, Mine +0, 0x640D = poi_oil_field, Oil Field +0, 0x640E = poi_park, Park +0, 0x640F = poi_post, Post +0, 0x6410 = poi_school, School +0, 0x6411 = poi_tower, Tower +0, 0x6412 = poi_trail, Trail +0, 0x6413 = poi_tunnel, Tunnel +0, 0x6414 = poi_drinking_water, Drink water +0, 0x6415 = town_ghost, Ghost Town +0, 0x6416 = poi_subdivision, Subdivision + +0, 0x6500 = poi_water_feature, Water Feature +0, 0x6501 = poi_water_feature, Arroyo +0, 0x6502 = poi_water_feature, Sand Bar +0, 0x6503 = poi_bay, Bay +0, 0x6504 = poi_bend, Bend +0, 0x6505 = poi_water_feature, Canal +0, 0x6506 = poi_water_feature, Channel +0, 0x6507 = poi_cove, Cove +0, 0x6508 = poi_water_feature, Falls +0, 0x6509 = poi_water_feature, Geyser +0, 0x650A = poi_water_feature, Glacier +0, 0x650B = poi_marine, Harbour +0, 0x650C = poi_island, Island +0, 0x650D = poi_water_feature, Lake +0, 0x650E = poi_water_feature, Rapids +0, 0x650F = poi_water_feature, Reservoir +0, 0x6510 = poi_water_feature, Sea +0, 0x6511 = poi_water_feature, Spring +0, 0x6512 = poi_water_feature, Stream +0, 0x6513 = poi_water_feature, Swamp + +0, 0x6600 = poi_land_feature, Land Feature +0, 0x6601 = poi_land_feature, Arch +0, 0x6602 = poi_land_feature, Area +0, 0x6603 = poi_land_feature, Basin +0, 0x6604 = poi_land_feature, Beach +0, 0x6605 = poi_land_feature, Bench +0, 0x6606 = poi_land_feature, Cape +0, 0x6607 = poi_land_feature, Cliff +0, 0x6608 = poi_land_feature, Crater +0, 0x6609 = poi_land_feature, Flat +0, 0x660A = poi_land_feature, Forest +0, 0x660B = poi_land_feature, Gap +0, 0x660C = poi_land_feature, Gut +0, 0x660D = poi_land_feature, Isthmus +0, 0x660E = poi_land_feature, Lava +0, 0x660F = poi_land_feature, Pillar +0, 0x6610 = poi_land_feature, Plain +0, 0x6611 = poi_land_feature, Range +0, 0x6612 = poi_land_feature, Reserve +0, 0x6613 = poi_land_feature, Ridge +0, 0x6614 = poi_land_feature, Rock +0, 0x6615 = poi_land_feature, Slope +0, 0x6616 = poi_land_feature, Summit +0, 0x6617 = poi_land_feature, Valley +0, 0x6618 = poi_land_feature, Woods + +# This are dublicated to 0x1700, 0x1800, 0x1900, 0x1A00, 0x1B00 +# fix them if you need them +0, 0x1600 = poi_marine_type, Beakon +0, 0x1601 = poi_marine_type, Fog Horn +0, 0x1602 = poi_marine_type, Radio Beacon +0, 0x1603 = poi_marine_type, Racon +0, 0x1604 = poi_marine_type, Day Beacon, red triangle +0, 0x1605 = poi_marine_type, Day Beacon, green square +0, 0x1606 = poi_marine_type, Day Beacon, white diamond +0, 0x1607 = poi_marine_type, unlit Navaid, white +0, 0x1608 = poi_marine_type, unlit Navaid, red +0, 0x1609 = poi_marine_type, unlit Navaid, green +0, 0x160a = poi_marine_type, unlit Navaid, black +0, 0x160b = poi_marine_type, unlit Navaid, yellow or amber +0, 0x160c = poi_marine_type, unlit Navaid, orange +0, 0x160d = poi_marine_type, unlit Navaid, multi colored +0, 0x160e = poi_marine_type, Navaid, unknown +0, 0x160f = poi_marine_type, lighted Navaid, white +0, 0x1610 = poi_marine_type, lighted Navaid, red +0, 0x1611 = poi_marine_type, lighted Navaid, green +0, 0x1612 = poi_marine_type, lighted Navaid, yellow or amber +0, 0x1613 = poi_marine_type, lighted Navaid, orange +0, 0x1614 = poi_marine_type, lighted Navaid, violet +0, 0x1615 = poi_marine_type, lighted Navaid, blue +0, 0x1616 = poi_marine_type, lighted Navaid, multi colored +# RGN2-4 types +# Marine types start +1, 0x0100 = point_unkn, Light +1, 0x0102 = point_unkn, Light with north topmark +1, 0x0103 = point_unkn, Light with south topmark +1, 0x0104 = point_unkn, Light with east topmark +1, 0x0105 = point_unkn, Light with west topmark +1, 0x0106 = point_unkn, Isolated danger light +1, 0x0107 = point_unkn, Port hand light +1, 0x0108 = point_unkn, Starboard hand light +1, 0x0109 = point_unkn, Special purpose light +1, 0x010a = point_unkn, Safe water light +1, 0x0200 = point_unkn, Buoy +1, 0x0201 = point_unkn, Buoy +1, 0x0202 = point_unkn, Buoy with north topmark +1, 0x0203 = point_unkn, Buoy with south topmark +1, 0x0204 = point_unkn, Buoy with east topmark +1, 0x0205 = point_unkn, Buoy with west topmark +1, 0x0206 = point_unkn, Beacon +1, 0x0207 = point_unkn, Spar buoy +1, 0x0208 = point_unkn, Isolated danger buoy +1, 0x0209 = point_unkn, Port hand buoy +1, 0x020a = point_unkn, Starboard hand buoy +1, 0x020b = point_unkn, Special purpose buoy +1, 0x020c = point_unkn, Safe water buoy +1, 0x020d = point_unkn, Platform buoy +1, 0x020e = point_unkn, Beacon with north topmark +1, 0x020f = point_unkn, Beacon with south north topmark +1, 0x0210 = point_unkn, Beacon with east topmark +1, 0x0211 = point_unkn, Beacon with west topmark +1, 0x0212 = point_unkn, Isolated danger beacon +1, 0x0213 = point_unkn, Port hand beacon +1, 0x0214 = point_unkn, Starboard hand beacon +1, 0x0215 = point_unkn, Special purpose beacon +1, 0x0216 = point_unkn, Mooring buoy +1, 0x0217 = point_unkn, Fixed point +1, 0x0218 = point_unkn, Pole +1, 0x0300 = point_unkn, Depth point +1, 0x0301 = point_unkn, Depth point invisible +1, 0x0302 = point_unkn, Depth point underscore +1, 0x0303 = point_unkn, Spot height +1, 0x0304 = point_unkn, Building +1, 0x0305 = point_unkn, Chimney +1, 0x0306 = point_unkn, Church +1, 0x0307 = point_unkn, Tanks +1, 0x0308 = point_unkn, Tower +1, 0x0309 = point_unkn, Rock +1, 0x030a = point_unkn, Triangulation point +1, 0x030b = point_unkn, Radio mast +1, 0x0400 = point_unkn, Isolated danger +1, 0x0401 = point_unkn, Obstruction +1, 0x0402 = point_unkn, Wreck +1, 0x0403 = point_unkn, Exposed wreck +1, 0x0404 = point_unkn, Well +1, 0x0405 = point_unkn, Foul +1, 0x0406 = point_unkn, Explosive +1, 0x0407 = point_unkn, Fish haven +1, 0x0408 = point_unkn, Obstruction that covers +1, 0x0409 = point_unkn, Marine farm +1, 0x040a = point_unkn, Dangerous rock +1, 0x040b = point_unkn, No bottom found +1, 0x040c = point_unkn, Exposed rock +1, 0x040d = point_unkn, Dangerous rock +1, 0x040e = point_unkn, Underwater rock (non-dangerous rock) +1, 0x040f = point_unkn, Shoal +1, 0x0500 = point_unkn, Label point +1, 0x0600 = point_unkn, Centered label +1, 0x0700 = point_unkn, Miscellaneous point +1, 0x0701 = point_unkn, Recommended anchorage +1, 0x0702 = point_unkn, Pilot boarding place +1, 0x0703 = point_unkn, Yacht harbour +1, 0x0704 = point_unkn, Pile +1, 0x0705 = point_unkn, Anchoring prohibited +1, 0x0706 = point_unkn, Fishing prohibited +1, 0x0707 = point_unkn, Precautionary area +1, 0x0708 = point_unkn, Radio report point +1, 0x0709 = point_unkn, Anchorage berths +1, 0x070a = point_unkn, Rescue station +1, 0x070b = point_unkn, Fishing harbour +1, 0x070c = point_unkn, Airport +1, 0x0800 = point_unkn, Information +1, 0x0901 = point_unkn, Bottom conditions +1, 0x0902 = point_unkn, Fishing information +1, 0x0903 = point_unkn, Facility +# NT types start +1, 0x0b00 = poi_dining, Dining(Other) +1, 0x0b04 = poi_dining, Dining(Chinese) +1, 0x0b05 = poi_dining, Dining (Deli/Bakery) +1, 0x0b06 = poi_dining, Dining (International) +1, 0x0b07 = poi_fastfood, Fast Food +1, 0x0b09 = poi_dining, Dining(Mexican) +1, 0x0b0a = poi_dining, Dining(Pizza) +1, 0x0b0b = poi_dining, Dining (Sea Food) +1, 0x0b0c = poi_dining, Dining (Steak/Grill) +1, 0x0b0d = poi_dining, Dining(Bagel/Donut) +1, 0x0b0e = poi_dining, Dining(Cafe/Diner) +1, 0x0c01 = poi_hotel, Hotel/Motel +1, 0x0c02 = poi_hotel, Bed & Breakfast inn +1, 0x0c03 = poi_camp_rv, Camping/RV-Park +1, 0x0c04 = poi_resort, Resort +1, 0x0d01 = poi_attraction, Amusement Park +1, 0x0d02 = poi_museum_history, Museum/History +1, 0x0d03 = poi_fair, Fair, Conference(point) +1, 0x0d04 = poi_scenic_area, Scenic Area +1, 0x0d05 = poi_school, School +1, 0x0d06 = poi_park, Park +1, 0x0d07 = poi_zoo, Zoo +1, 0x0d08 = poi_stadium, Sportpark, Stadium +1, 0x0d0a = poi_bar, Wine Bar +1, 0x0d14 = poi_hospital, Hospital +1, 0x0e01 = poi_theater, Theater +1, 0x0e02 = poi_bar, Bar +1, 0x0e03 = poi_cinema, Cinema +1, 0x0e04 = poi_casino, Casino +1, 0x0e05 = poi_golf, Golf +1, 0x0e06 = poi_skiing, Skiing Center/Lift +1, 0x0e07 = poi_bar, Night Bar/Piano +1, 0x0e08 = poi_icesport, Ice/Sporting +1, 0x0e09 = poi_swimming, Swimming +1, 0x0e0a = poi_sport, Sports(point) +1, 0x0f00 = poi_hotel, Hotel(Other) +1, 0x0f01 = poi_shop_department, Department Store +1, 0x0f02 = poi_shop_grocery, Grocery +1, 0x0f03 = poi_hotel, Hotel/Motel +1, 0x0f04 = poi_resort, Resort +1, 0x0f05 = poi_pharmacy, Pharmacy +1, 0x1001 = poi_fuel, Fuel/Gas +1, 0x1002 = poly_car_parking, Parking Lot +1, 0x1003 = poi_autoservice, Car Repair +1, 0x1004 = poi_airport, Airport +1, 0x1005 = poi_post, Post Office +1, 0x1006 = poi_bank, Bank +1, 0x1008 = poi_bus_station, Bus station +1, 0x1009 = poi_marina, SeaPort +1, 0x100a = poi_wrecker, Wrecker Service +1, 0x100b = poi_car_parking, Parking +1, 0x100d = poi_car_dealer_parts, Car Dealer(point) +1, 0x100e = poi_car_wash, Car Wash +1, 0x1011 = poi_bussines_service, Business Service +1, 0x1012 = poi_communication, Communication +1, 0x1013 = poi_repair_service, Repair Service +1, 0x1014 = poi_social_service, Social Service +1, 0x1015 = poi_public_utilities, Utility +1, 0x1101 = poi_police, Police Station +1, 0x1102 = poi_hospital, Hospital +1, 0x1104 = poi_justice, Justice +1, 0x1107 = poi_public_office, Public Office +1, 0x1400 = town_label_1e5, Megapolis (10M +) +1, 0x1401 = town_label_5e4, Megapolis (5-10M) +1, 0x1402 = town_label_2e4, Big City (2-5M) +1, 0x1403 = town_label_5e3, Big City (0.5-1M) +1, 0x1404 = town_label_2e3, City (200-500k) +1, 0x1405 = town_label_1e3, City (100-200k) +1, 0x1406 = town_label_5e2, City (50-100k) +1, 0x1405 = town_label_2e2, City (20-50k) +1, 0x1406 = town_label_1e2, City (10-20k) +1, 0x1407 = town_label_5e1, Small City (5-10k) +1, 0x1408 = town_label_2e1, Small City (2-5k) +1, 0x1409 = town_label_1e1, Village (1-2k) +1, 0x140a = town_label_5e0, Village (500-1000) +1, 0x140b = town_label_2e0, Village (200-500) +1, 0x140c = town_label_1e0, Village (100-200) +1, 0x140d = town_label_0e0, Village (0-100) +1, 0x1500 = poi_personal_service, Personal Service +1, 0x1501 = poi_bussines_service, Bussines Service + + +POLYLINE +0, 0x00 = street_1_land, Road +0, 0x01 = highway_land, Major HWY thick +0, 0x02 = street_4_land, Principal HWY-thick +0, 0x03 = street_2_land, Principal HWY-medium +0, 0x04 = street_3_city, Arterial Road-medium +0, 0x05 = street_4_city, Arterial Road-thick +0, 0x06 = street_2_city, Road-thin +0, 0x07 = street_1_city, Alley-thick +0, 0x08 = ramp, Ramp +0, 0x09 = ramp, Ramp highspeed +0, 0x0a = street_0, Unpaved Road-thin +0, 0x0b = ramp, Major HWY Connector-thick +0, 0x0c = roundabout, Roundabout +0, 0x0d = street_unkn, Custom defined street 1 +0, 0x0e = street_unkn, Custom defined street 2 +0, 0x0f = street_unkn, Custom defined street 3 +0, 0x10 = street_unkn, Custom defined street 4 +0, 0x11 = street_unkn, Custom defined street 5 +0, 0x12 = street_unkn, Custom defined street 6 +0, 0x13 = street_unkn, Custom defined street 7 +0, 0x14 = rail, Railroad +0, 0x15 = water_line, Shoreline +0, 0x16 = street_nopass, Trail +0, 0x18 = water_line, Stream-thin +0, 0x19 = time_zone, Time-Zone +0, 0x1a = ferry, Ferry +0, 0x1b = ferry, Ferry +0, 0x1c = border_country, Political Boundary +0, 0x1d = border_country, County Boundary +0, 0x1e = border_country, Intl. Boundary +0, 0x1f = water_line, River +0, 0x20 = height_line_1, Land Contour (thin) Height in feet +0, 0x21 = height_line_2, Land Contour (medium) Height in feet +0, 0x22 = height_line_3, Land Contour (thick) Height in feet +0, 0x23 = depth_line_1, Depth Contour (thin) Depth in feet +0, 0x24 = depth_line_2, Depth Contour (medium) Depth in feet +0, 0x25 = depth_line_3, Depth Contour (thick) Depth in feet +0, 0x26 = water_line, Intermittent River +0, 0x27 = street_nopass, Airport Runway +0, 0x28 = pipeline, Pipeline +0, 0x29 = powerline, Powerline +0, 0x2a = marine_boundary, Marine Boundary (no line) +0, 0x2b = marine_hazard, Marine Hazard (no line) +# RGN2-4 +# Marine +1, 0x0100 = street_unkn, Miscellaneous line +1, 0x0101 = street_unkn, Line +1, 0x0102 = street_unkn, Cartographic line +1, 0x0103 = street_unkn, Road +1, 0x0104 = street_unkn, Clearing line +1, 0x0105 = street_unkn, Contour line +1, 0x0106 = street_unkn, Overhead cable +1, 0x0107 = street_unkn, Bridge +1, 0x0108 = street_unkn, Recommended route +1, 0x0109 = street_unkn, Chart border +1, 0x0300 = street_unkn, Depth contour +1, 0x0301 = street_unkn, Depth contour value +1, 0x0307 = street_unkn, Intertidal zone border +1, 0x0400 = street_unkn, Obstruction line +1, 0x0401 = street_unkn, Submarine cable +1, 0x0402 = street_unkn, Submarine pipeline +1, 0x0403 = street_unkn, Pile barrier +1, 0x0404 = street_unkn, Fishing stakes +1, 0x0405 = street_unkn, Supply pipeline area +1, 0x0406 = street_unkn, Submarine cable area +1, 0x0407 = street_unkn, Dumping ground +1, 0x0408 = street_unkn, Explosive dumping ground +1, 0x0409 = street_unkn, Danger line +1, 0x040a = street_unkn, Overhead cable +1, 0x040b = street_unkn, Submerged construction +1, 0x040c = street_unkn, Pier/jetty +1, 0x0500 = street_unkn, Restriction +1, 0x0501 = street_unkn, Anchoring prohibited +1, 0x0502 = street_unkn, Fishing prohibited +1, 0x0503 = street_unkn, Prohibited area +1, 0x0504 = street_unkn, Military practice area +1, 0x0505 = street_unkn, Anchoring and fishing prohibited +1, 0x0506 = street_unkn, Limit of nature reservation +1, 0x0507 = street_unkn, Restricted area +1, 0x0508 = street_unkn, Minefield +1, 0x0600 = street_unkn, Miscellaneous line +1, 0x0601 = street_unkn, Cartographic line +1, 0x0602 = street_unkn, Traffic separation line +1, 0x0603 = street_unkn, International maritime boundary +1, 0x0604 = street_unkn, Straight territorial sea baseline +1, 0x0605 = street_unkn, Seaward limit of territorial sea +1, 0x0606 = street_unkn, Anchorage area +1, 0x0607 = street_unkn, Quarantine anchorage area +1, 0x0608 = street_unkn, Fishery zone +1, 0x0609 = street_unkn, Swept area +1, 0x060a = street_unkn, Traffic separation zone +1, 0x060b = street_unkn, Limit of exclusive economic zone +1, 0x060c = street_unkn, Established direction of traffic flow +1, 0x060d = street_unkn, Recommended direction of traffic flow +1, 0x060e = street_unkn, Harbour limit +1, 0x060f = street_unkn, Inadequately surveyed area +1, 0x0610 = street_unkn, Inshore traffic zone +1, 0x0611 = street_unkn, Limit of traffic lane +1, 0x0701 = street_unkn, River channel +1, 0x0702 = street_unkn, Submerged object +1, 0x0703 = street_unkn, Submerged object +1, 0x0704 = street_unkn, Submerged object +1, 0x0705 = street_unkn, Submerged object +1, 0x0706 = street_unkn, Chart boundary +# NT types +1, 0x0a00 = water_line, River +1, 0x0a01 = water_line, Small River +1, 0x0a19 = street_unkn, Dry Creek +1, 0x0b04 = border_country, Country Border + + +POLYGONE +0, 0x01 = poly_town, City (>200k) +0, 0x02 = poly_town, City (<200k) +0, 0x03 = poly_town, Village +0, 0x04 = poly_military_zone, Military +0, 0x05 = poly_car_parking, Parking Lot +0, 0x06 = poly_car_parking, Parking Garage +0, 0x07 = poly_airport, Airport +0, 0x08 = poly_commercial_center, Shopping Center +0, 0x09 = poly_marine, Marina +0, 0x0a = poly_university, University/College +0, 0x0b = poly_hospital, Hospital +0, 0x0c = poly_industry, Industrial +0, 0x0d = area, Reservation +0, 0x0e = poly_airport, Airport Runway +0, 0x13 = area_unspecified, Man made area +0, 0x14 = poly_national_park, National park +0, 0x15 = poly_national_park, National park +0, 0x16 = poly_nature_park, National park +0, 0x17 = poly_park, City Park +0, 0x18 = poly_golf_course, Golf +0, 0x19 = poly_sport, Sport +0, 0x1a = poly_cemetery, Cemetery +0, 0x1e = poly_nature_park, State Park +0, 0x1f = poly_park, State Park +0, 0x20 = poly_park, State Park +0, 0x28 = poly_water, Ocean +0, 0x29 = poly_water, Water Reservour +0, 0x32 = poly_water, Sea +0, 0x3b = poly_water, Water Reservour +0, 0x3c = poly_water, Lake (250-600 km2) +0, 0x3d = poly_water, Lake (77-250 km2) +0, 0x3e = poly_water, Lake (25-77 km2) +0, 0x3f = poly_water, Lake (11-25 km2) +0, 0x40 = poly_water, Lake (0.25-11 km2) +0, 0x41 = poly_water, Lake (<0.25 km2) +0, 0x42 = poly_water, Lake (>3.3k km2) +0, 0x43 = poly_water, Lake (1.1-3.3k km2) +0, 0x44 = poly_water, Lake (0.6-1.1k km2) +0, 0x45 = poly_water, Water Reservour +0, 0x46 = poly_water, River (>1km) +0, 0x47 = poly_water, River (200m-1km) +0, 0x48 = poly_water, River (40-200m) +0, 0x49 = poly_water, River (<40m) +0, 0x4a = area, Definition Area +0, 0x4b = area, Background +0, 0x4c = poly_water, Intermittent River/Lake +0, 0x4d = poly_glacier, Glaciers +0, 0x4e = plantation, Orchard or plantation +0, 0x4f = poly_scrub, Scrub +0, 0x50 = poly_wood, Woods +0, 0x51 = poly_wetland, Wetland +0, 0x52 = tundra, Tundra +0, 0x53 = poly_flats, Flats +# RGN2-4 +# Marine +1, 0x0100 = street_unkn, Land - white +1, 0x0101 = street_unkn, Land - non-urban +1, 0x0102 = street_unkn, Land - urban +1, 0x0103 = street_unkn, Chart exclusion area +1, 0x0104 = street_unkn, Chart background +1, 0x0105 = street_unkn, Bridge +1, 0x0300 = street_unkn, Depth area - white 1 +1, 0x0301 = street_unkn, Intertidal zone +1, 0x0302 = street_unkn, Depth area - blue 1 +1, 0x0303 = street_unkn, Depth area - blue 2 +1, 0x0304 = street_unkn, Depth area - blue 3 +1, 0x0305 = street_unkn, Depth area - blue 4 +1, 0x0306 = street_unkn, Depth area - blue 5 +1, 0x0307 = street_unkn, Depth area - white +1, 0x0400 = street_unkn, Obstruction (invisible) +1, 0x0401 = street_unkn, Submarine cable (invisible) +1, 0x0402 = street_unkn, Submarine pipeline (invisible) +1, 0x0403 = street_unkn, Pile barrier (invisible) +1, 0x0404 = street_unkn, Fishing stakes (invisible) +1, 0x0405 = street_unkn, Supply pipeline area/line (invisible) +1, 0x0406 = street_unkn, Submarine cable area/line (invisible) +1, 0x0407 = street_unkn, Dumping ground (invisible) +1, 0x0408 = street_unkn, Explosive dumping ground (invisible) +1, 0x0409 = street_unkn, Danger line (invisible) +1, 0x040a = street_unkn, Overhead cable (invisible) +1, 0x040b = street_unkn, Submerged construction (invisible) +1, 0x040c = street_unkn, Pier/jetty (invisible) +1, 0x0500 = street_unkn, Restriction area/line (invisible) +1, 0x0501 = street_unkn, Anchoring prohibited (invisible) +1, 0x0502 = street_unkn, Fishing prohibited (invisible) +1, 0x0503 = street_unkn, Prohibited area (invisible) +1, 0x0504 = street_unkn, Military practice area (invisible) +1, 0x0505 = street_unkn, Anchoring and fishing prohibited (invisible) +1, 0x0506 = street_unkn, Limit of nature reservation (invisible) +1, 0x0507 = street_unkn, Restricted area (invisible) +1, 0x0508 = street_unkn, Minefield (invisible) +1, 0x0600 = street_unkn, Miscellaneous area +1, 0x0601 = street_unkn, Cartographic area +1, 0x0602 = street_unkn, Traffic separation area +1, 0x0603 = street_unkn, International maritime boundary +1, 0x0604 = street_unkn, Straight territorial sea baseline +1, 0x0605 = street_unkn, Seaward limit of territorial sea +1, 0x0606 = street_unkn, Anchorage area +1, 0x0607 = street_unkn, Quarantine anchorage area +1, 0x0608 = street_unkn, Fishery zone +1, 0x0609 = street_unkn, Swept area +1, 0x060a = street_unkn, Traffic separation zone +1, 0x060b = street_unkn, Limit of exclusive economic zone +1, 0x060c = street_unkn, Established direction of traffic flow +1, 0x0701 = street_unkn, Fishing area +1, 0x0702 = street_unkn, Restricted area +1, 0x0703 = street_unkn, Anchorage area +1, 0x0704 = street_unkn, Fishing Hot Spots chart +# NT types +1, 0x0800 = poly_town, Large City +1, 0x0801 = poly_town, City +1, 0x0900 = poly_plaza, Square/Place +1, 0x0902 = poly_car_parking, Car Park (Parking Lot) +1, 0x0904 = poly_airport, Airport +1, 0x090d = poly_sports_stadium, Stage like circus/sport/stadium +1, 0x090e = poly_cemetery, Graveyard/Cemetery +1, 0x0a01 = poly_national_park, National Park +1, 0x0a04 = poly_park, City Park +1, 0x0a05 = poly_nature_park, Nature Park +1, 0x0a06 = street_unkn, Dam/Man made/ ?? +1, 0x0b02 = poly_water, Sea +1, 0x0b03 = poly_water, Water Reservour +1, 0x0b07 = poly_water, Dam/Lake +1, 0x0b08 = poly_water, Dam/Lake +1, 0x0b0c = poly_water, River diff --git a/map/garmin/gentypes.c b/map/garmin/gentypes.c new file mode 100644 index 00000000..16682a55 --- /dev/null +++ b/map/garmin/gentypes.c @@ -0,0 +1,148 @@ +/* + Copyright (C) 2007 Alexander Atanasov <aatanasov@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + 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 + + Garmin and MapSource are registered trademarks or trademarks + of Garmin Ltd. or one of its subsidiaries. + +*/ + +/* +File format is: + +POINT +GROUP,0x0100 = town_label_1e5, Megapolis (10M +) +GROUP,0x0200 = town_label_5e4, Megapolis (5-10M) +... +GROUP,0x1e00-0x1e3f = district_label, District, Province, State Name +... +POLYLINE +GROUP,0x00 = street_1_land, Road +GROUP,0x01 = highway_land, Major HWY thick +GROUP,0x02 = street_4_land, Principal HWY-thick +GROUP,0x03 = street_2_land, Principal HWY-medium +.... +POLYGONE +GROUP,0x01 = town_poly, City (>200k) +GROUP,0x02 = town_poly, City (<200k) +GROUP,0x03 = town_poly, Village + +GROUP is +0 - good old garmin types in RGN1 +1 - NT types in RGN2-4 5 is completely unknown yet + */ + +#include <stdio.h> +#include <string.h> +#include <malloc.h> +#include <unistd.h> +#include "item.h" +#include "gar2navit.h" + +#define dlog(x, y...) fprintf(stderr, ## y) +/* +static int add_def(struct gar2nav_conv *conv, int type, unsigned short minid, + unsigned short maxid, unsigned int routable, char *ntype, + char *descr) +*/ + +static void print_header(FILE *fp) +{ + fprintf(fp, "// This is autogenerated file -- DO NOT EDIT\n"); + fprintf(fp, "struct gar2nav_conv *g2n_default_conv(void)\n" + "{\n" + "\tstruct gar2nav_conv *conv;\n" + "\n" + "\tconv = calloc(1, sizeof(*conv));\n" + "\tif (!conv)\n" + "\t\treturn conv;\n"); +} + +static int load_types_file(char *file, char *out) +{ + char buf[4096]; + char descr[4096]; + char ntype[4096]; + FILE *fp; + unsigned int minid, maxid, group; + int rc; + int type = -1; + FILE *fpout = stdout; + + fp = fopen(file, "r"); + if (!fp) + return -1; + if (out) { + fpout = fopen(out, "w"); + if (!fpout) + return -1; + } + print_header(fpout); + while (fgets(buf, sizeof(buf), fp)) { + if (*buf == '#' || *buf == '\n') + continue; + if (!strncasecmp(buf, "POINT", 5)) { + type = 1; + continue; + } else if (!strncasecmp(buf, "POI", 3)) { + type = 1; + continue; + } else if (!strncasecmp(buf, "POLYLINE", 8)) { + type = 2; + continue; + } else if (!strncasecmp(buf, "POLYGONE", 8)) { + type = 3; + continue; + } + + rc = sscanf(buf, "%d, 0x%04X - 0x%04X = %[^\t , ] , %[^\n]", + &group, &minid, &maxid, ntype, descr); + if (rc != 5) { + maxid = 0; + rc = sscanf(buf, "%d, 0x%04X = %[^\t, ], %[^\n]", + &group,&minid, ntype, descr); + if (rc != 4) { + dlog(1, "Invalid line rc=%d:[%s]\n",rc, buf); + dlog(1, "minid=%04X ntype=[%s] des=[%s]\n", + minid, ntype, descr); + continue; + } + } + + fprintf(fpout, "\tadd_def(conv, %d, %#.04x, %#.04x, %d, \"%s\", \"%s\");\n", + type, minid, maxid, group, ntype, descr); + } + fprintf(fpout, "\treturn conv;\n"); + fprintf(fpout, "}\n"); + fclose(fp); + if (out) + fclose(fpout); + return 1; +} + +int main(int argc, char **argv) +{ + if (argc!=3) { + fprintf(stderr, "Usage: %s garmintypes.txt outfile.c\n", + argv[0]); + return -1; + } + if (load_types_file(argv[1], argv[2]) < 0) { + unlink(argv[2]); + return -1; + } + return 0; +} diff --git a/map/garmin_img/Makefile.am b/map/garmin_img/Makefile.am new file mode 100644 index 00000000..dbbcd9b5 --- /dev/null +++ b/map/garmin_img/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_garmin_img +modulemap_LTLIBRARIES = libmap_garmin_img.la +libmap_garmin_img_la_SOURCES = garmin_img.c +libmap_garmin_img_la_LDFLAGS = -module -avoid-version diff --git a/map/garmin_img/garmin_img.c b/map/garmin_img/garmin_img.c new file mode 100644 index 00000000..58fe489d --- /dev/null +++ b/map/garmin_img/garmin_img.c @@ -0,0 +1,1513 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "config.h" +#include "plugin.h" +#include "data.h" +#include "projection.h" +#include "map.h" +#include "maptype.h" +#include "item.h" +#include "attr.h" +#include "coord.h" +#include "transform.h" +#include <stdio.h> +#include "attr.h" +#include "coord.h" + +struct file { + FILE *f; + int offset; +}; + + +int shift=5; +int subdiv_next=0x10; + + +static void * +file_read(struct file *f, int offset, int size) +{ + void *ptr; + int ret; + + ptr=calloc(size, 1); + if (! ptr) + return ptr; + fseek(f->f, f->offset+offset, SEEK_SET); + ret=fread(ptr, size, 1, f->f); + if (ret != 1) { + printf("fread %d vs %d offset %d+%d(0x%x)\n", ret, size, f->offset, offset,offset); + g_assert(1==0); + } + return ptr; +} + +static void +file_free(void *ptr) +{ + free(ptr); +} + +struct offset_len { + int offset; + int length; +} __attribute ((packed)); + +static void +dump_offset_len(struct offset_len *off_len) +{ + printf("offset: 0x%x(%d) length 0x%x(%d)\n", off_len->offset, off_len->offset, off_len->length, off_len->length); +} + +struct timestamp { + short creation_year; + char creation_month; + char creation_day; + char creation_hour; + char creation_minute; + char creation_second; +} __attribute__((packed)); + +struct img_header { + char xor; + char zero1[9]; + char update_month; + char update_year; + char zero2[3]; + char checksum[1]; + char signature[7]; + char unknown1[1]; + char unknown2[2]; + char unknown3[2]; + char unknown4[2]; + char unknown5[2]; + char zero3[25]; + struct timestamp ts; + char unknown6; + char map_file_identifier[7]; + char unknown12; + char map_description1[20]; + short unknown13; + short unknown14; + char e1; + char e2; + char other[413]; + char zero4[512]; + char unknown7; + char unknown8[11]; + int file_offset; + char unknown9; + char unknown10[15]; + char unknown11[480]; +} __attribute__((packed)); + +static void +dump_ts(struct timestamp *ts) +{ + printf("%d-%02d-%02d %02d:%02d:%02d\n", ts->creation_year, ts->creation_month, ts->creation_day, ts->creation_hour, ts->creation_minute, ts->creation_second); +} + +#if 0 +static void +dump_img(struct img_header *img_hdr) +{ + printf("signature: '%s'\n", img_hdr->signature); + printf("creation: "); + dump_ts(&img_hdr->ts); + printf("map_file_identifier: '%s'\n", img_hdr->map_file_identifier); + printf("file_offset: 0x%x\n", img_hdr->file_offset); + printf("e1: 0x%x(%d)\n", img_hdr->e1, img_hdr->e1); + printf("e2: 0x%x(%d)\n", img_hdr->e2, img_hdr->e2); + printf("offset 0x%x\n", (int) &img_hdr->e1 - (int) img_hdr); + printf("size %d\n", sizeof(*img_hdr)); +} +#endif + +struct fat_block { + char flag; + char filename[8]; + char type[3]; + int size; + char zero1; + char part; + char zero[14]; + unsigned short blocks[240]; +} __attribute__((packed)); + +#if 0 +static void +dump_fat_block(struct fat_block *fat_blk) +{ + int i=0; + char name[9]; + char type[4]; + printf("flag: 0x%x(%d)\n", fat_blk->flag, fat_blk->flag); + strcpy(name, fat_blk->filename); + name[8]='\0'; + strcpy(type, fat_blk->type); + type[3]='\0'; + printf("name: '%s.%s'\n", name, type); + printf("size: 0x%x(%d)\n", fat_blk->size, fat_blk->size); + printf("part: 0x%x(%d)\n", fat_blk->part, fat_blk->part); + printf("blocks: "); + while (i < 240) { + printf("0x%x(%d) ",fat_blk->blocks[i], fat_blk->blocks[i]); + if (fat_blk->blocks[i] == 0xffff) + break; + i++; + } + printf("size: %d\n", sizeof(*fat_blk)); + +} +#endif + +struct file_header { + short header_len; + char type[10]; + char unknown1; + char unknown2; + struct timestamp ts; +} __attribute__((packed)); + +static void +dump_file(struct file_header *fil_hdr) +{ + printf("header_len: %d\n", fil_hdr->header_len); + printf("type: '%s'\n", fil_hdr->type); + printf("unknown1: 0x%x(%d)\n", fil_hdr->unknown1, fil_hdr->unknown1); + printf("unknown2: 0x%x(%d)\n", fil_hdr->unknown2, fil_hdr->unknown2); + printf("creation: "); + dump_ts(&fil_hdr->ts); + printf("size %d\n", sizeof(*fil_hdr)); +} + +struct region_header { + struct file_header fil_hdr; + struct offset_len offset_len; +} __attribute__((packed)); + +#if 0 +static void +dump_region(struct region_header *rgn_hdr) +{ + dump_offset_len(&rgn_hdr->offset_len); +} +#endif + +struct map_priv { + int id; + char *filename; +}; + +struct map_rect_priv { + struct coord_rect r; + int limit; + + struct file tre; + struct tree_header *tre_hdr; + struct file rgn; + struct region_header *rgn_hdr; + struct file lbl; + struct label_header *lbl_hdr; + char *label; + + int subdiv_level_count; + int subdiv_pos; + char *subdiv; + + int rgn_offset; + int rgn_end; + struct rgn_point *pnt; + struct rgn_poly *ply; + unsigned char *ply_data; + int ply_bitpos; + int ply_bitcount; + int ply_lngbits; + int ply_latbits; + int ply_lng; + int ply_lat; + int ply_lnglimit; + int ply_latlimit; + int ply_lngsign; + int ply_latsign; + struct offset_len rgn_items[4]; + int rgn_type; + + int count; + + FILE *f; + long pos; + char line[256]; + int attr_pos; + enum attr_type attr_last; + char attrs[256]; + char attr[256]; + double lat,lng; + char lat_c,lng_c; + int eoc; + struct map_priv *m; + struct item item; +}; + +static int map_id; + +static int +contains_coord(char *line) +{ + return g_ascii_isdigit(line[0]); +} + +static int debug=1; + +static int +get_tag(char *line, char *name, int *pos, char *ret) +{ + int len,quoted; + char *p,*e,*n; + + if (debug) + printf("get_tag %s from %s\n", name, line); + if (! name) + return 0; + len=strlen(name); + if (pos) + p=line+*pos; + else + p=line; + for(;;) { + while (*p == ' ') { + p++; + } + if (! *p) + return 0; + n=p; + e=index(p,'='); + if (! e) + return 0; + p=e+1; + quoted=0; + while (*p) { + if (*p == ' ' && !quoted) + break; + if (*p == '"') + quoted=1-quoted; + p++; + } + if (e-n == len && !strncmp(n, name, len)) { + e++; + len=p-e; + if (e[0] == '"') { + e++; + len-=2; + } + strncpy(ret, e, len); + ret[len]='\0'; + if (pos) + *pos=p-line; + return 1; + } + } + return 0; +} + +static void +get_line(struct map_rect_priv *mr) +{ + mr->pos=ftell(mr->f); + fgets(mr->line, 256, mr->f); +} + +static void +map_destroy_garmin_img(struct map_priv *m) +{ + if (debug) + printf("map_destroy_garmin_img\n"); + g_free(m); +} + +static char * +map_charset_garmin_img(struct map_priv *m) +{ + return "iso8859-1"; +} + +static enum projection +map_projection_garmin_img(struct map_priv *m) +{ + return projection_garmin; +} + +struct label_data_offset { + struct offset_len offset_len; + char multiplier; + char data; +} __attribute ((packed)); + +#if 0 +static void +dump_label_data_offset(struct label_data_offset *lbl_dat) +{ + dump_offset_len(&lbl_dat->offset_len); + printf("multiplier 0x%x(%d)\n", lbl_dat->multiplier, lbl_dat->multiplier); + printf("data 0x%x(%d)\n", lbl_dat->data, lbl_dat->data); +} +#endif + +struct label_data { + struct offset_len offset_len; + short size; + int zero; +} __attribute ((packed)); + +static void +dump_label_data(struct label_data *lbl_dat) +{ + dump_offset_len(&lbl_dat->offset_len); + printf("size 0x%x(%d)\n", lbl_dat->size, lbl_dat->size); +} + +struct tree_header { + struct file_header fil_hdr; + char boundary[12]; + struct offset_len level; + struct offset_len subdivision; + struct label_data copyright; + struct offset_len tre7; + short unknown1; + char zero1; + struct label_data polyline; + struct label_data polygon; + struct label_data point; + int mapid; +}; + +static void +dump_tree_header(struct tree_header *tre_hdr) +{ + printf("tree_header:\n"); + dump_file(&tre_hdr->fil_hdr); + printf("level: "); dump_offset_len(&tre_hdr->level); + printf("subdivision: "); dump_offset_len(&tre_hdr->subdivision); + printf("copyright: "); dump_label_data(&tre_hdr->copyright); + printf("polyline: "); dump_label_data(&tre_hdr->polyline); + printf("polygon: "); dump_label_data(&tre_hdr->polygon); + printf("point: "); dump_label_data(&tre_hdr->point); + printf("len: 0x%x(%d)\n", sizeof(*tre_hdr), sizeof(*tre_hdr)); +} + +struct label_header { + struct file_header fil_hdr; + struct label_data_offset label; + struct label_data country; + struct label_data region; + struct label_data city; + struct label_data poi_index; + struct label_data_offset poi_properties; + short zero1; + char zero2; + struct label_data poi_types; + struct label_data zip; + struct label_data hway; + struct label_data exit; + struct label_data hway_data; + int unknown1; + short unknown2; + struct offset_len sort_descriptor; + struct label_data lbl13; + struct label_data lbl14; +} __attribute((packed)); + +#if 0 +static void +dump_label(struct label_header *lbl_hdr) +{ + dump_file(&lbl_hdr->fil_hdr); + printf("label:\n"); + dump_label_data_offset(&lbl_hdr->label); + printf("country:\n"); + dump_label_data(&lbl_hdr->country); + printf("region:\n"); + dump_label_data(&lbl_hdr->region); + printf("city:\n"); + dump_label_data(&lbl_hdr->city); + printf("poi_index:\n"); + dump_label_data(&lbl_hdr->poi_index); + printf("poi_properties:\n"); + dump_label_data_offset(&lbl_hdr->poi_properties); + printf("poi_types:\n"); + dump_label_data(&lbl_hdr->poi_types); + printf("zip:\n"); + dump_label_data(&lbl_hdr->zip); + printf("hway:\n"); + dump_label_data(&lbl_hdr->hway); + printf("exit:\n"); + dump_label_data(&lbl_hdr->exit); + printf("hway_data:\n"); + dump_label_data(&lbl_hdr->hway_data); + printf("lbl13:\n"); + dump_label_data(&lbl_hdr->lbl13); + printf("lbl14:\n"); + dump_label_data(&lbl_hdr->lbl14); + printf("len: 0x%x(%d)\n", sizeof(*lbl_hdr), sizeof(*lbl_hdr)); +} +#endif + +struct triple { + unsigned char data[3]; +} __attribute((packed)); + +static unsigned int +triple_u(struct triple *t) +{ + return t->data[0] | (t->data[1] << 8) | (t->data[2] << 16); +} + +static int +triple(struct triple *t) +{ + int ret=t->data[0] | (t->data[1] << 8) | (t->data[2] << 16); + if (ret > 1<<23) + ret=ret-(1<<24); + return ret; +} + +static void +dump_triple_u(struct triple *t) +{ + int val=triple_u(t); + printf("0x%x(%d)\n", val, val); +} + +struct tcoord { + struct triple lng,lat; +} __attribute((packed)); + +static void +dump_tcoord(struct tcoord *t) +{ + printf ("0x%x(%d),0x%x(%d)\n", triple_u(&t->lng), triple_u(&t->lng), triple_u(&t->lat), triple_u(&t->lat)); +} + +struct level { + unsigned char zoom; + unsigned char bits_per_coord; + unsigned short subdivisions; +} __attribute((packed)); + +static void +dump_level(struct level *lvl) +{ + printf("level:\n"); + printf("\tzoom 0x%x(%d)\n", lvl->zoom, lvl->zoom); + printf("\tbits_per_coord 0x%x(%d)\n", lvl->bits_per_coord, lvl->bits_per_coord); + printf("\tsubdivisions 0x%x(%d)\n", lvl->subdivisions, lvl->subdivisions); +} + +struct subdivision { + struct triple rgn_offset; + unsigned char types; + struct tcoord center; + unsigned short width; + unsigned short height; + unsigned short next; +} __attribute((packed)); + +static void +dump_subdivision(struct subdivision *sub) +{ + printf("subdivision:\n"); + printf("\trgn_offset: "); dump_triple_u(&sub->rgn_offset); + printf("\ttypes: 0x%x(%d)\n", sub->types, sub->types); + printf("\tcenter: "); dump_tcoord(&sub->center); + printf("\tsize: 0x%x(%d)x0x%x(%d) %s\n",sub->width & 0x7fff, sub->width & 0x7fff, sub->height, sub->height, sub->width & 0x8000 ? "Terminating" : ""); + printf("\tnext: 0x%x(%d)\n",sub->next, sub->next); + + printf("\tlen: 0x%x(%d)\n", sizeof(*sub), sizeof(*sub)); +} + +struct rgn_point { + unsigned char info; + struct triple lbl_offset; + short lng_delta; + short lat_delta; + unsigned char subtype; +} __attribute((packed)); + +static void +dump_point(struct rgn_point *pnt) +{ + printf("point:\n"); + printf("\tinfo 0x%x(%d)\n", pnt->info, pnt->info); + printf("\tlbl_offset 0x%x(%d)\n", triple_u(&pnt->lbl_offset), triple_u(&pnt->lbl_offset)); + printf("\tlng_delta 0x%x(%d)\n", pnt->lng_delta, pnt->lng_delta); + printf("\tlat_delta 0x%x(%d)\n", pnt->lat_delta, pnt->lat_delta); + printf("\tsubtype 0x%x(%d)\n", pnt->subtype, pnt->subtype); + printf("\tlen: 0x%x(%d)\n", sizeof(*pnt), sizeof(*pnt)); +} + +struct rgn_poly { + unsigned char info; + struct triple lbl_offset; + short lng_delta; + short lat_delta; + union { + struct { + unsigned char bitstream_len; + unsigned char bitstream_info; + } __attribute((packed)) p1; + struct { + unsigned short bitstream_len; + unsigned char bitstream_info; + } __attribute((packed)) p2; + } __attribute((packed)) u; +} __attribute((packed)); + +static void +dump_poly(struct rgn_poly *ply) +{ + printf("poly:\n"); + printf("\tinfo 0x%x(%d)\n", ply->info, ply->info); + printf("\tlbl_offset 0x%x(%d)\n", triple_u(&ply->lbl_offset), triple_u(&ply->lbl_offset)); + printf("\tlng_delta 0x%x(%d)\n", ply->lng_delta, ply->lng_delta); + printf("\tlat_delta 0x%x(%d)\n", ply->lat_delta, ply->lat_delta); + if (ply->info & 0x80) { + printf("\tbitstream_len 0x%x(%d)\n", ply->u.p2.bitstream_len, ply->u.p2.bitstream_len); + printf("\tbitstream_info 0x%x(%d)\n", ply->u.p2.bitstream_info, ply->u.p2.bitstream_info); + } else { + printf("\tbitstream_len 0x%x(%d)\n", ply->u.p1.bitstream_len, ply->u.p1.bitstream_len); + printf("\tbitstream_info 0x%x(%d)\n", ply->u.p1.bitstream_info, ply->u.p1.bitstream_info); + } + printf("\tlen: 0x%x(%d)\n", sizeof(*ply), sizeof(*ply)); +} + +static void +dump_hex(void *ptr, int len) +{ + unsigned char *c=ptr; + while (len--) { + printf("%02x ", *c++); + } + printf("\n"); +} + +static void +dump_hex_r(void *ptr, int len, int rec) +{ + unsigned char *c=ptr; + int l=rec; + while (len--) { + printf("%02x ", *c++); + if (! --l) { + printf("\n"); + l=rec; + } + } + printf("\n"); +} + +#if 0 +static void +dump_label_offset(struct map_rect_priv *mr, int offset) +{ + void *p; + p=file_read(&mr->lbl, mr->lbl_hdr->label.offset_len.offset+offset, 128); + printf("%s\n", (char *)p); +} +#endif + + +#if 0 +static void +dump_region_item(struct subdivision *sub, struct file *rgn, struct map_rect_priv *mr) +{ + int offset,item_offset,i,j; + unsigned short count=0; + unsigned short *offsets[4]; + unsigned short *file_offsets; + struct rgn_point *pnt; + + offset=triple_u(&sub->rgn_offset)+mr->rgn_hdr->offset_len.offset; + file_offsets=file_read(rgn, offset, 90*sizeof(unsigned short)); + printf("0x%x ", offset); dump_hex(file_offsets, 90); + for (i=0 ; i < 4 ; i++) { + printf("i=%d\n", i); + if (sub->types & (0x10 << i)) { + if (count) { + offsets[i]=&file_offsets[count-1]; + } else + offsets[i]=&count; + count++; + } else + offsets[i]=NULL; + + } + count--; + count*=2; + for (i=0 ; i < 4 ; i++) { + printf("i=%d\n", i); + if (offsets[i]) { + printf("offset[%d]=0x%x(%d)\n", i, *offsets[i], *offsets[i]); + switch (i) { + case 0: + printf("point\n"); + break; + case 1: + printf("indexed point\n"); + break; + case 2: + printf("polyline\n"); + break; + case 3: + printf("polygon\n"); + break; + } + item_offset=offset+*offsets[i]; + switch (i) { + case 0: + case 1: + for (j = 0 ; j < 10 ; j++) { + struct coord_geo g; + char buffer[1024]; + double conv=180.0/(1UL<<23); + pnt=file_read(rgn, item_offset, sizeof(*pnt)*20); + // printf("0x%x ", item_offset); dump_hex(pnt, 32); + dump_point(pnt); + g.lng=(triple(&sub->center.lng)+(pnt->lng_delta << shift))*conv; + g.lat=(triple(&sub->center.lat)+(pnt->lat_delta << shift))*conv; + printf("%f %f\n", g.lng, g.lat); + coord_format(g.lat,g.lng,DEGREES_MINUTES_SECONDS, + buffer,sizeof(buffer)); + printf("%s\n", buffer); + dump_label_offset(mr, triple_u(&pnt->lbl_offset)); + if (pnt->info & 0x80) + item_offset+=sizeof(*pnt); + else + item_offset+=sizeof(*pnt)-1; + } + } + } else { + printf("offset[%d] doesn't exist\n", i); + } + } + file_free(file_offsets); +} + +#endif + +static void +dump_levels(struct map_rect_priv *mr) +{ + int i,offset; + struct level *lvl; + + offset=mr->tre_hdr->level.offset; + for (i = 0 ; i < mr->tre_hdr->level.length/sizeof(*lvl) ; i++) { + lvl=file_read(&mr->tre, offset, sizeof(*lvl)); + dump_level(lvl); + offset+=sizeof(*lvl); + } +} + +#if 0 +static void +dump_tree(struct file *f, struct file *rgn, struct map_rect_priv *mr) +{ + struct tree_header *tre_hdr; + struct subdivision *sub; + int i,offset; + + tre_hdr=file_read(f, 0, sizeof(*tre_hdr)); + dump_tree_header(tre_hdr); + offset=tre_hdr->subdivision.offset; + sub=file_read(f, offset, sizeof(*sub)); + dump_subdivision(sub); + offset+=sizeof(*sub); + for (i = 1 ; i < tre_hdr->subdivision.length/sizeof(*sub) ; i++) { + printf("i=%d\n", i); + sub=file_read(f, offset, sizeof(*sub)); + dump_subdivision(sub); + dump_region_item(sub, rgn, mr); + if (sub->width & 0x8000) + break; + offset+=sizeof(*sub); + } + file_free(tre_hdr); +} +#endif + +#if 0 +static void +dump_labels(struct file *f) +{ + struct label_header *lbl_hdr; + + lbl_hdr=file_read(f, 0, sizeof(*lbl_hdr)); + printf("**labels**\n"); + dump_label(lbl_hdr); + file_free(lbl_hdr); +#if 0 + labels=alloca(lbl_hdr.label_length); + file_read(f, lbl_hdr.label_offset, labels, lbl_hdr.label_length); + l=labels; + while (l < labels+lbl_hdr.label_length) { + printf("'%s'(%d)\n", l, strlen(l)); + l+=strlen(l)+1; + } +#endif + +} +#endif + +static void +garmin_img_coord_rewind(void *priv_data) +{ +} + +static void +parse_line(struct map_rect_priv *mr) +{ + int pos=0; + sscanf(mr->line,"%lf %c %lf %c %n",&mr->lat,&mr->lat_c,&mr->lng,&mr->lng_c,&pos); + if (pos < strlen(mr->line)) { + strcpy(mr->attrs, mr->line+pos); + } +} + +static int +get_bits(struct map_rect_priv *mr, int bits) +{ + unsigned long ret; + ret=L(*((unsigned long *)(mr->ply_data+mr->ply_bitpos/8))); + ret >>= (mr->ply_bitpos & 7); + ret &= (1 << bits)-1; + mr->ply_bitpos+=bits; + return ret; +} + +static int +garmin_img_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + struct subdivision *sub=(struct subdivision *)(mr->subdiv+mr->subdiv_pos); + int ret=0; + int debug=0; + if (debug) + printf("garmin_img_coord_get %d\n",count); + if (debug) + dump_subdivision(sub); + while (count--) { + if (mr->rgn_type < 2) { + c->x=triple(&sub->center.lng)+(mr->pnt->lng_delta << shift); + c->y=triple(&sub->center.lat)+(mr->pnt->lat_delta << shift); + } else { + if (! mr->ply_bitpos) { + if (mr->ply->info & 0x80) { + mr->ply_bitcount=mr->ply->u.p2.bitstream_len*8; + mr->ply_lngbits=mr->ply->u.p2.bitstream_info & 0xf; + mr->ply_latbits=mr->ply->u.p2.bitstream_info >> 4; + } else { + mr->ply_bitcount=mr->ply->u.p1.bitstream_len*8; + mr->ply_lngbits=mr->ply->u.p1.bitstream_info & 0xf; + mr->ply_latbits=mr->ply->u.p1.bitstream_info >> 4; + } + if (mr->ply_lngbits <= 9) + mr->ply_lngbits+=2; + if (mr->ply_latbits <= 9) + mr->ply_latbits+=2; + if (! get_bits(mr,1)) { + mr->ply_lngbits+=1; + mr->ply_lngsign=0; + } else + if (get_bits(mr, 1)) + mr->ply_lngsign=-1; + else + mr->ply_lngsign=1; + if (! get_bits(mr,1)) { + mr->ply_latbits+=1; + mr->ply_latsign=0; + } else + if (get_bits(mr, 1)) + mr->ply_latsign=-1; + else + mr->ply_latsign=1; + mr->ply_lnglimit=1 << (mr->ply_lngbits-1); + mr->ply_latlimit=1 << (mr->ply_latbits-1); + mr->ply_lng=mr->ply->lng_delta; + mr->ply_lat=mr->ply->lat_delta; + if (debug) + printf("lngbits %d latbits %d bitcount %d\n", mr->ply_lngbits, mr->ply_latbits, mr->ply_bitcount); + c->x=0; + c->y=0; + } else { + if (mr->ply_bitpos + mr->ply_lngbits + mr->ply_latbits > mr->ply_bitcount) { + if (debug) + printf("out of bits %d + %d + %d >= %d\n", mr->ply_bitpos, mr->ply_lngbits, mr->ply_latbits, mr->ply_bitcount); + return ret; + } + c->x=0; + c->y=0; + int x,y; + for (;;) { + x=get_bits(mr,mr->ply_lngbits); + if (debug) + printf("x %d ", x); + if (mr->ply_lngsign || x != mr->ply_lnglimit) + break; + c->x += x-1; + } + if (mr->ply_lngsign) { + c->x=x*mr->ply_lngsign; + } else { + if (x >= mr->ply_lnglimit) + c->x = x - (mr->ply_lnglimit << 1) - c->x; + else + c->x +=x; + } + for (;;) { + y=get_bits(mr,mr->ply_latbits); + if (debug) + printf("y %d ", y); + if (mr->ply_latsign || y != mr->ply_latlimit) + break; + c->y += y-1; + } + if (mr->ply_latsign) { + c->y=y*mr->ply_latsign; + } else { + if (y >= mr->ply_latlimit) + c->y = y - (mr->ply_latlimit << 1) - c->y; + else + c->y +=y; + } + mr->ply_lng += c->x; + mr->ply_lat += c->y; + } + if (debug) + printf(": x %d y %d\n", c->x, c->y); + + c->x=triple(&sub->center.lng)+(mr->ply_lng << shift); + c->y=triple(&sub->center.lat)+(mr->ply_lat << shift); + } +#if 0 + c->x-=0x6f160; + c->y-=0x181f59; + c->x+=0x168ca1; + c->y+=0x68d815; +#endif + c++; + ret++; + if (mr->rgn_type < 2) + return ret; + } + return ret; +} + +static char * +get_label_offset(struct map_rect_priv *mr, int offset) +{ + g_assert(offset < mr->lbl_hdr->label.offset_len.length); + return file_read(&mr->lbl, mr->lbl_hdr->label.offset_len.offset+offset, 128); +} + +static void +garmin_img_attr_rewind(void *priv_data) +{ +} + +static int +garmin_img_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + int debug=0; + + if (debug) + printf("garmin_img_attr_get\n"); + if (attr_type == attr_label) { + if (debug) + printf("garmin_img_attr_get label\n"); + attr->type=attr_type; + if (mr->rgn_type < 2) { + if (mr->label) + file_free(mr->label); + mr->label=get_label_offset(mr, triple_u(&mr->pnt->lbl_offset) & 0x3fffff); + attr->u.str=mr->label; + } else { + attr->u.str=""; + } + return 1; + } + return 0; +} + +static struct item_methods methods_garmin_img = { + garmin_img_coord_rewind, + garmin_img_coord_get, + garmin_img_attr_rewind, + garmin_img_attr_get, +}; + +static int rgn_next_type(struct map_rect_priv *mr) +{ + while (mr->rgn_type < 3) { + mr->rgn_type++; + if (mr->rgn_items[mr->rgn_type].offset && mr->rgn_items[mr->rgn_type].length != 0) { + mr->rgn_offset=mr->rgn_items[mr->rgn_type].offset; + mr->rgn_end=mr->rgn_offset+mr->rgn_items[mr->rgn_type].length; + return 0; + } + } + return 1; +} + +static int +sub_next(struct map_rect_priv *mr, int next) +{ + int i,offset,first=-1,last=-1,count=-1; + int end; + unsigned short *offsets; + int debug=0; + + if (mr->subdiv_level_count <= 0) + return 1; + if (debug) + printf("%d left\n", mr->subdiv_level_count); + mr->subdiv_level_count--; + +#if 0 + if (next && mr->subdiv[mr->subdiv_current].width & 0x8000) + return 1; +#endif + if (debug) + dump_hex_r(mr->subdiv+mr->subdiv_pos, 64, 14); + mr->subdiv_pos+=next; + if (debug) + printf("subdiv_pos 0x%x\n", mr->subdiv_pos); + if (mr->subdiv_pos > mr->tre_hdr->subdivision.length) + return 1; + struct subdivision *sub=(struct subdivision *)(mr->subdiv+mr->subdiv_pos); + offset=triple_u(&sub->rgn_offset)+mr->rgn_hdr->offset_len.offset; + if (debug) { + printf("offset=0x%x\n", offset); + dump_subdivision(sub); + } + offsets=file_read(&mr->rgn, offset, 3*sizeof(unsigned short)); + + if (! next) + next=subdiv_next; + if (mr->subdiv_pos+next < mr->tre_hdr->subdivision.length) + end=triple_u(&((struct subdivision *)(mr->subdiv+mr->subdiv_pos+next))->rgn_offset)+mr->rgn_hdr->offset_len.offset; + else + end=mr->rgn_hdr->offset_len.offset+mr->rgn_hdr->offset_len.length; + if (debug) { + dump_subdivision(sub); + dump_hex(offsets, 6); + } + for (i=0 ; i < 4 ; i++) { + if (debug) + printf("i=%d ", i); + if (sub->types & (0x10 << i)) { + if (debug) + printf("+ "); + if (first == -1) { + first=i; + mr->rgn_items[i].offset=offset; + if (debug) + printf("\n"); + } else { + mr->rgn_items[i].offset=offset+offsets[count]; + if (debug) + printf("0x%x\n", offsets[count]); + mr->rgn_items[last].length=mr->rgn_items[i].offset-mr->rgn_items[last].offset; + } + last=i; + count++; + } else { + if (debug) + printf("-\n"); + mr->rgn_items[i].offset=0; + mr->rgn_items[i].length=0; + } + + } + if (first != -1) { + mr->rgn_items[first].offset+=count*2; + mr->rgn_items[first].length-=count*2; + mr->rgn_items[last].length=end-mr->rgn_items[last].offset; + } + if (debug) { + for (i=0 ; i < 4 ; i++) { + printf("%d 0x%x 0x%x\n", i, mr->rgn_items[i].offset, mr->rgn_items[i].length); + } + } + mr->rgn_type=-1; + rgn_next_type(mr); + if (debug) + printf("*** offset 0x%x\n", mr->rgn_offset); + file_free(offsets); + return 0; +} + +int item_count; + +static struct map_rect_priv * +map_rect_new_garmin_img(struct map_priv *map, struct coord_rect *r, struct layer *layers, int limit) +{ + struct map_rect_priv *mr; + struct img_header img; + + if (debug) + printf("map_rect_new_garmin_img\n"); + mr=g_new0(struct map_rect_priv, 1); + mr->m=map; + if (r) + mr->r=*r; + mr->limit=limit; + mr->item.id_hi=0; + mr->item.id_lo=0; + mr->item.meth=&methods_garmin_img; + mr->item.priv_data=mr; + mr->f=fopen(map->filename, "r"); + + fread(&img, sizeof(img), 1, mr->f); +#if 0 + dump_img(&img); + for (i = 0 ; i < (img.file_offset-sizeof(img))/sizeof(fat_blk) ; i++) { + fread(&fat_blk, sizeof(fat_blk), 1, mr->f); + if (!fat_blk.flag) + break; + dump_fat_block(&fat_blk); + } +#endif + mr->rgn.offset=0xa*2048; + mr->rgn.f=mr->f; + mr->rgn_hdr=file_read(&mr->rgn, 0, sizeof(*mr->rgn_hdr)); + + mr->tre.offset=0x62b*2048; + mr->tre.f=mr->f; + mr->tre_hdr=file_read(&mr->tre, 0, sizeof(*mr->tre_hdr)); + + mr->lbl.offset=0x64a*2048; + mr->lbl.f=mr->f; + mr->lbl_hdr=file_read(&mr->lbl, 0, sizeof(*mr->lbl_hdr)); + + mr->subdiv=file_read(&mr->tre, mr->tre_hdr->subdivision.offset, mr->tre_hdr->subdivision.length); +#if 0 + dump_hex_r(mr->subdiv, mr->tre_hdr->subdivision.length, 16); +#endif + dump_tree_header(mr->tre_hdr); + + dump_levels(mr); + + + printf("limit=%d\n", limit); + if (limit < 3) { + mr->subdiv_pos=0; + mr->subdiv_level_count=1; + shift=11; + } else if (limit < 6) { + mr->subdiv_pos=1*sizeof(struct subdivision); + mr->subdiv_level_count=5; + shift=9; + } else if (limit < 8) { + mr->subdiv_pos=6*sizeof(struct subdivision); + mr->subdiv_level_count=9; + shift=7; + } else if (limit < 10) { + mr->subdiv_pos=15*sizeof(struct subdivision); + mr->subdiv_level_count=143; + shift=5; + } else { + mr->subdiv_pos=158*sizeof(struct subdivision); + mr->subdiv_level_count=4190; + shift=2; + subdiv_next=14; + } + +#if 0 + mr->rgn_offset=triple_u(&mr->subdiv[mr->subdiv_current].rgn_offset)+mr->rgn_hdr->offset_len.offset+4; + mr->rgn_type=1; + mr->rgn_end=mr->rgn_offset+20*8; +#endif + mr->count=0; + item_count=0; + +#if 0 + printf("*** offset 0x%x\n", 0x656c-mr->rgn.offset); + printf("*** offset 0x%x\n", mr->rgn_offset); +#endif +#if 1 + sub_next(mr, 0); +#endif +#if 0 + { + struct rgn_point *pnt; + int i; + int offset=0x65cc; + for (i = 0 ; i < 26 ; i++) { + pnt=file_read(&mr->rgn, 0x656c+8*i-mr->rgn.offset, sizeof(*pnt)); + // dump_hex(pnt, sizeof(*pnt)); + dump_point(pnt); + dump_label_offset(mr, triple_u(&pnt->lbl_offset)); + } + } + exit(0); +#endif +#if 0 + dump_tree(&mr->tre,&mr->rgn,mr); +#endif + +#if 0 + f.offset=0x64a*2048; + f.f=mr->f; + dump_labels(&f); +#endif +#if 0 + fseek(mr->f, img.file_offset, SEEK_SET); + fread(&fil, sizeof(fil), 1, mr->f); + dump_file(&fil); + fread(&rgn, sizeof(rgn), 1, mr->f); + dump_region(&rgn); + fseek(mr->f, rgn.data_length, SEEK_CUR); + fread(&fil, sizeof(fil), 1, mr->f); + dump_file(&fil); +#endif + return mr; +} + + +static void +map_rect_destroy_garmin_img(struct map_rect_priv *mr) +{ + fclose(mr->f); + g_free(mr); +} + + +static struct item * +map_rect_get_item_garmin_img(struct map_rect_priv *mr) +{ + char *p,type[256]; + int ptype; + int debug=0; + + item_count++; + + if (debug) + printf("map_rect_get_item_garmin_img\n"); + for (;;) { + if (mr->rgn_offset < mr->rgn_end) { + if (debug) + printf("data available\n"); + if (mr->rgn_type >= 2) { + int len; + if (debug) + printf("polyline %d\n", mr->count); + if (mr->ply) + file_free(mr->ply); + mr->ply=file_read(&mr->rgn, mr->rgn_offset, sizeof(*mr->ply)*3); + if(triple_u(&mr->ply->lbl_offset) >= mr->lbl_hdr->label.offset_len.length) { + printf("item_count %d\n", item_count); + dump_poly(mr->ply); + dump_hex(mr->ply, 32); + printf("%d vs %d\n", triple_u(&mr->ply->lbl_offset), mr->lbl_hdr->label.offset_len.length); + } + g_assert(triple_u(&mr->ply->lbl_offset) < mr->lbl_hdr->label.offset_len.length); + if (debug) { + dump_hex(mr->ply, 16); + dump_poly(mr->ply); + } + if (mr->ply_data) + file_free(mr->ply_data); + mr->rgn_offset+=10; + if (mr->ply->info & 0x80) { + mr->rgn_offset++; + len=mr->ply->u.p2.bitstream_len; + } else + len=mr->ply->u.p1.bitstream_len; + + mr->ply_data=file_read(&mr->rgn, mr->rgn_offset, len); + mr->rgn_offset += len; + mr->ply_bitpos=0; + // dump_hex(mr->ply_data, 32); + if (mr->rgn_type == 3) { + switch(mr->ply->info & 0x7f) { + case 0x1: /* large urban area (>200k) */ + mr->item.type=type_town_poly; + break; + case 0xd: /* reservation */ + mr->item.type=type_park_poly; + break; + case 0xe: /* airport runway */ + mr->item.type=type_airport_poly; + break; + case 0x14: /* national park */ + mr->item.type=type_park_poly; + break; + case 0x32: /* sea */ + case 0x3d: /* large lake (77-250km2) */ + case 0x4c: /* intermittend water */ + mr->item.type=type_water_poly; + break; + case 0x4b: /* background */ + continue; + default: + printf("unknown polygon: 0x%x\n", mr->ply->info); + mr->item.type=type_street_3_city; + } + } else { + switch(mr->ply->info & 0x3f) { + case 0x1: /* major highway */ + mr->item.type=type_highway_land; + break; + case 0x2: /* principal highway */ + mr->item.type=type_street_3_land; + break; + case 0x6: /* residental street */ + mr->item.type=type_street_2_land; + break; + case 0x16: /* walkway/trail */ + mr->item.type=type_street_1_land; + break; + case 0x1e: /* international boundary */ + mr->item.type=type_border_country; + break; + case 0x20: /* minor land contour 1/10 */ + mr->item.type=type_height_line_1; + break; + case 0x21: /* major land contour 1/2 */ + mr->item.type=type_height_line_2; + break; + default: + printf("unknown polyline: 0x%x\n", mr->ply->info); + mr->item.type=type_street_3_city; + } + } + return &mr->item; + } + if (mr->pnt) + file_free(mr->pnt); + mr->pnt=file_read(&mr->rgn, mr->rgn_offset, sizeof(*mr->pnt)); + mr->item.type=type_none; + int subtype=mr->pnt->subtype; + if (mr->pnt->lbl_offset.data[2] & 0x80) + mr->rgn_offset+=9; + else { + mr->rgn_offset+=8; + subtype=0; + } + switch(mr->pnt->info) { + case 0x3: /* large city 2-5M */ + mr->item.type=type_town_label_2e6; + break; + case 0xa: /* small city/town 10-20k */ + mr->item.type=type_town_label_1e4; + break; + case 0xd: /* settlement 1-2K */ + mr->item.type=type_town_label_1e3; + break; + case 0x11: /* settlement less 100 */ + mr->item.type=type_town_label_5e1; + break; + case 0x1c: + switch(subtype) { + case 0x01: + mr->item.type=type_poi_wreck; + break; + } + break; + case 0x20: + mr->item.type=type_highway_exit; + break; + case 0x25: + mr->item.type=type_poi_toll_booth; + break; + case 0x2b: + switch(subtype) { + case 0x01: + mr->item.type=type_poi_hotel; + break; + case 0x03: + mr->item.type=type_poi_camp_rv; + break; + } + break; + case 0x2c: + switch(subtype) { + case 0x00: + mr->item.type=type_poi_attraction; + break; + case 0x02: + mr->item.type=type_poi_museum_history; + break; + } + break; + case 0x2e: + mr->item.type=type_poi_shopping; + break; + case 0x2f: + switch(subtype) { + case 0x01: + mr->item.type=type_poi_fuel; + break; + case 0x07: + mr->item.type=type_poi_car_dealer_parts; + break; + case 0x0b: + mr->item.type=type_poi_car_parking; + break; + case 0x15: + mr->item.type=type_poi_public_utilities; + break; + } + break; + case 0x30: + switch(subtype) { + case 0x02: + mr->item.type=type_poi_hospital; + break; + } + break; + case 0x43: + mr->item.type=type_poi_marina; + break; + case 0x46: + mr->item.type=type_poi_bar; + break; + case 0x48: + mr->item.type=type_poi_camping; + break; + case 0x49: + mr->item.type=type_poi_park; + break; + case 0x4a: + mr->item.type=type_poi_picnic; + break; + case 0x59: /* airport */ + mr->item.type=type_poi_airport; + break; + case 0x64: + switch(subtype) { + case 0x1: + mr->item.type=type_poi_bridge; + break; + case 0x2: + mr->item.type=type_poi_building; + break; + case 0x15: + mr->item.type=type_town_ghost; + break; + } + break; + case 0x65: + switch(subtype) { + case 0x0: + mr->item.type=type_poi_water_feature; + break; + case 0xc: + mr->item.type=type_poi_island; + break; + case 0xd: + mr->item.type=type_poi_lake; + break; + } + break; + case 0x66: + switch(subtype) { + case 0x0: + mr->item.type=type_poi_land_feature; + break; + case 0x6: + mr->item.type=type_poi_cape; + break; + case 0x14: + mr->item.type=type_poi_rock; + break; + } + break; + } + if (mr->item.type == type_none) { + printf("unknown point: 0x%x 0x%x\n", mr->pnt->info, mr->pnt->subtype); + dump_point(mr->pnt); + printf("label: %s\n", get_label_offset(mr, triple_u(&mr->pnt->lbl_offset) & 0x3fffff)); + mr->item.type=type_town_label; + } + return &mr->item; + } + if (debug) + printf("out of data for type\n"); + if (rgn_next_type(mr)) { + if (debug) + printf("out of data for region\n"); + if (sub_next(mr, subdiv_next)) { + if (debug) + printf("out of data for subdivision\n"); + return NULL; + } + } + } +} + +static struct item * +map_rect_get_item_byid_garmin_img(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + fseek(mr->f, id_lo, SEEK_SET); + get_line(mr); + mr->item.id_hi=id_hi; + return map_rect_get_item_garmin_img(mr); +} + +static struct map_methods map_methods_garmin_img = { + projection_garmin, + "iso8859-1", + map_destroy_garmin_img, + map_charset_garmin_img, + map_projection_garmin_img, + map_rect_new_garmin_img, + map_rect_destroy_garmin_img, + map_rect_get_item_garmin_img, + map_rect_get_item_byid_garmin_img, +}; + +static struct map_priv * +map_new_garmin_img(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + struct attr *data=attr_search(attrs, NULL, attr_data); + if (! data) + return NULL; + + *meth=map_methods_garmin_img; + m=g_new(struct map_priv, 1); + m->id=++map_id; + m->filename=g_strdup(data->u.str); + return m; +} + +void +plugin_init(void) +{ + plugin_register_map_type("garmin_img", map_new_garmin_img); +} + diff --git a/map/mg/Makefile.am b/map/mg/Makefile.am new file mode 100644 index 00000000..7fcef2de --- /dev/null +++ b/map/mg/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_mg +if PLUGINS + modulemap_LTLIBRARIES = libmap_mg.la +else + noinst_LTLIBRARIES = libmap_mg.la +endif +libmap_mg_la_SOURCES = map.c block.c town.c tree.c poly.c street.c mg.h +libmap_mg_la_LDFLAGS = -module -avoid-version diff --git a/map/mg/block.c b/map/mg/block.c new file mode 100644 index 00000000..98886572 --- /dev/null +++ b/map/mg/block.c @@ -0,0 +1,298 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdio.h> +#include <string.h> +#include "debug.h" +#include "mg.h" + + +int block_lin_count,block_idx_count,block_active_count,block_mem,block_active_mem; + +struct block_index_item { + /*unsigned int blocknum; + unsigned int blocks;*/ + unsigned char p[8]; +}; +static inline unsigned int block_index_item_get_blocknum(struct block_index_item * blk) { unsigned char *p = blk->p; return get_u32(&p); } +static inline unsigned int block_index_item_get_blocks(struct block_index_item * blk) { unsigned char *p = blk->p+4; return get_u32(&p); } + +struct block_index { +/* unsigned int blocks; + unsigned int size; + unsigned int next; + struct block_index_item list[0];*/ + unsigned char p[12]; +}; +static inline unsigned int block_index_get_blocks(struct block_index * blk) { unsigned char *p = blk->p; return get_u32(&p); } +static inline unsigned int block_index_get_size(struct block_index * blk) { unsigned char *p = blk->p+4; return get_u32(&p); } +static inline unsigned int block_index_get_next(struct block_index * blk) { unsigned char *p = blk->p+8; return get_u32(&p); } +static inline struct block_index_item * block_index_get_list(struct block_index * blk) { return (struct block_index_item *)(blk->p+12); } + +static struct block * +block_get(unsigned char **p) +{ + struct block *ret=(struct block *)(*p); + *p += sizeof(*ret); + return ret; +} + + +static struct block * +block_get_byid(struct file *file, int id, unsigned char **p_ret) +{ + struct block_index *blk_idx; + int blk_num,max; + + + blk_idx=(struct block_index *)(file->begin+0x1000); + max=(block_index_get_size(blk_idx)-sizeof(struct block_index))/sizeof(struct block_index_item); + block_mem+=24; + while (id >= max) { + blk_idx=(struct block_index *)(file->begin+block_index_get_next(blk_idx)*512); + id-=max; + } + blk_num=block_index_item_get_blocknum(&block_index_get_list(blk_idx)[id]); + + *p_ret=file->begin+blk_num*512; + return block_get(p_ret); +} + +int +block_get_byindex(struct file *file, int idx, struct block_priv *blk) +{ + dbg(1,"idx=%d\n", idx); + blk->b=block_get_byid(file, idx, &blk->p); + blk->block_start=(unsigned char *)(blk->b); + blk->p_start=blk->p; + blk->end=blk->block_start+block_get_size(blk->b); + + return 1; +} + +static void +block_setup_tags(struct map_rect_priv *mr) +{ + int len; + unsigned char *p,*t; + char *str; + + mr->b.binarytree=0; + + p=mr->file->begin+0x0c; + while (*p) { + str=get_string(&p); + len=get_u32_unal(&p); + t=p; + /* printf("String '%s' len %d\n", str, len); */ + if (! strcmp(str,"FirstBatBlock")) { + /* printf("%ld\n", get_u32_unal(&t)); */ + } else if (! strcmp(str,"MaxBlockSize")) { + /* printf("%ld\n", get_u32_unal(&t)); */ + } else if (! strcmp(str,"FREE_BLOCK_LIST")) { + /* printf("%ld\n", get_u32_unal(&t)); */ + } else if (! strcmp(str,"TotalRect")) { + mr->b.b_rect.lu.x=get_u32_unal(&t); + mr->b.b_rect.lu.y=get_u32_unal(&t); + mr->b.b_rect.rl.x=get_u32_unal(&t); + mr->b.b_rect.rl.y=get_u32_unal(&t); + /* printf("0x%x,0x%x-0x%x,0x%x\n", mr->b.b_rect.lu.x, mr->b.b_rect.lu.y, mr->b.b_rect.rl.x, mr->b.b_rect.rl.y); */ + } else if (! strcmp(str,"Version")) { + /* printf("0x%lx\n", get_u32_unal(&t)); */ + } else if (! strcmp(str,"Categories")) { + /* printf("0x%x\n", get_u16(&t)); */ + } else if (! strcmp(str,"binaryTree")) { + mr->b.binarytree=get_u32_unal(&t); + /* printf("%d\n", mr->b.binarytree); */ + } else if (! strcmp(str,"CategorySets")) { + /* printf("0x%x\n", get_u16(&t)); */ + } else if (! strcmp(str,"Kommentar")) { + /* printf("%s\n", get_string(&t)); */ + } + p+=len; + } +} + +#if 0 +static void +block_rect_print(struct coord_rect *r) +{ + printf ("0x%x,0x%x-0x%x,0x%x (0x%x,0x%x)", r->lu.x, r->lu.y, r->rl.x, r->rl.y, r->lu.x/2+r->rl.x/2,r->lu.y/2+r->rl.y/2); +} +#endif + +static void +block_rect_same(struct coord_rect *r1, struct coord_rect *r2) +{ + dbg_assert(r1->lu.x==r2->lu.x); + dbg_assert(r1->lu.y==r2->lu.y); + dbg_assert(r1->rl.x==r2->rl.x); + dbg_assert(r1->rl.y==r2->rl.y); +} + +int +block_init(struct map_rect_priv *mr) +{ + mr->b.block_num=-1; + mr->b.bt.b=NULL; + mr->b.bt.next=0; + block_setup_tags(mr); + if (mr->b.binarytree) { + mr->b.bt.next=mr->b.binarytree; + mr->b.bt.p=NULL; + mr->b.bt.block_count=0; + } + if (mr->cur_sel && !coord_rect_overlap(&mr->cur_sel->u.c_rect, &mr->b.b_rect)) + return 0; + return block_next(mr); +} + + +int +block_next_lin(struct map_rect_priv *mr) +{ + struct coord_rect r; + for (;;) { + block_lin_count++; + block_mem+=sizeof(struct block *); + mr->b.block_num++; + if (! mr->b.block_num) + mr->b.p=mr->file->begin+0x2000; + else + mr->b.p=mr->b.block_start+block_get_blocks(mr->b.b)*512; + if (mr->b.p >= mr->file->end) { + dbg(1,"end of blocks %p vs %p\n", mr->b.p, mr->file->end); + return 0; + } + mr->b.block_start=mr->b.p; + mr->b.b=block_get(&mr->b.p); + mr->b.p_start=mr->b.p; + mr->b.end=mr->b.block_start+block_get_size(mr->b.b); + if (block_get_count(mr->b.b) == -1) { + dbg(1,"empty blocks\n"); + return 0; + } + block_get_r(mr->b.b, &r); + if (!mr->cur_sel || coord_rect_overlap(&mr->cur_sel->u.c_rect, &r)) { + block_active_count++; + block_active_mem+=block_get_blocks(mr->b.b)*512-sizeof(struct block *); + dbg(1,"block ok\n"); + return 1; + } + dbg(2,"block not in cur_sel\n"); + } +} + +int +block_next(struct map_rect_priv *mr) +{ + int blk_num,coord,r_h,r_w; + struct block_bt_priv *bt=&mr->b.bt; + struct coord_rect r; + + if (!mr->b.binarytree || ! mr->cur_sel) + return block_next_lin(mr); + for (;;) { + if (! bt->p) { + dbg(1,"block 0x%x\n", bt->next); + if (bt->next == -1) + return 0; + bt->b=block_get_byid(mr->file, bt->next, &bt->p); + bt->end=(unsigned char *)mr->b.bt.b+block_get_size(mr->b.bt.b); + bt->next=block_get_next(bt->b); + bt->order=0; + dbg(1,"size 0x%x next 0x%x\n", block_get_size(bt->b), block_get_next(bt->b)); + if (! mr->b.bt.block_count) { +#if 0 + if (debug) { + printf("idx rect "); + block_rect_print(&mr->b.bt.b->r); + } +#endif + block_get_r(bt->b, &bt->r); + bt->r_curr=bt->r; + coord=get_u32(&mr->b.bt.p); + } else { + bt->p=(unsigned char *)bt->b+0xc; + } + bt->block_count++; + } + while (mr->b.bt.p < mr->b.bt.end) { + block_idx_count++; + blk_num=get_u32(&mr->b.bt.p); + coord=get_u32(&mr->b.bt.p); + block_mem+=8; + dbg(1,"%p vs %p coord 0x%x ", mr->b.bt.end, mr->b.bt.p, coord); + dbg(1,"block 0x%x", blk_num); + + r_w=bt->r_curr.rl.x-bt->r_curr.lu.x; + r_h=bt->r_curr.lu.y-bt->r_curr.rl.y; +#if 0 + if (debug) { + printf(" rect1 "); + block_rect_print(&bt->r_curr); + printf(" %dx%d", r_w, r_h); + } +#endif + mr->b.b=NULL; + if (blk_num != -1) { + block_mem+=8; + if (coord_rect_overlap(&mr->cur_sel->u.c_rect, &bt->r_curr)) { + mr->b.b=block_get_byid(mr->file, blk_num, &mr->b.p); + mr->b.block_num=blk_num; + dbg_assert(mr->b.b != NULL); + mr->b.block_start=(unsigned char *)(mr->b.b); + mr->b.p_start=mr->b.p; + mr->b.end=mr->b.block_start+block_get_size(mr->b.b); + block_get_r(mr->b.b, &r); + block_rect_same(&r, &bt->r_curr); + } + } + if (coord != -1) { + bt->stack[bt->stackp]=bt->r_curr; + if (r_w > r_h) { + bt->r_curr.rl.x=coord; + bt->stack[bt->stackp].lu.x=coord+1; + } else { + bt->r_curr.lu.y=coord; + bt->stack[bt->stackp].rl.y=coord+1; + } + bt->stackp++; + dbg_assert(bt->stackp < BT_STACK_SIZE); + } else { + if (bt->stackp) { + bt->stackp--; + bt->r_curr=bt->stack[bt->stackp]; + } else { + bt->r_curr=bt->r; + bt->order++; + if (bt->order > 100) + return 0; + } + } + if (mr->b.b) { + block_active_count++; + block_active_mem+=block_get_blocks(mr->b.b)*512; + return 1; + } + } + bt->p=NULL; + } + return 0; +} diff --git a/map/mg/map.c b/map/mg/map.c new file mode 100644 index 00000000..976d6850 --- /dev/null +++ b/map/mg/map.c @@ -0,0 +1,609 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdio.h> +#include <string.h> +#include "config.h" +#include "debug.h" +#include "plugin.h" +#include "maptype.h" +#include "mg.h" + + +GList *maps; + +static struct country_isonum { + int country; + int isonum; + int postal_len; + char *postal_prefix; +} country_isonums[]={ + { 1,203}, + { 2,703}, + { 7,674}, + { 11,233}, + { 12,268}, + { 13,428}, + { 14,440}, + { 15,498}, + { 16,643}, + { 17,804}, + { 18,112}, + { 20,818}, + { 30,300}, + { 31,528}, + { 32, 56}, + { 33,250}, + { 34,724}, + { 36,348}, + { 39,380}, + { 40,642}, + { 41,756}, + { 43, 40}, + { 44,826}, + { 45,208}, + { 46,752}, + { 47,578}, + { 48,616}, + { 49,276,5,"D@@"}, + { 50,292}, + { 51,620}, + { 52,442}, + { 53,372}, + { 54,352}, + { 55, 8}, + { 56,470}, + { 57,196}, + { 58,246}, + { 59,100}, + { 61,422}, + { 62, 20}, + { 63,760}, + { 66,682}, + { 71,434}, + { 72,376}, + { 73,275}, + { 75,438}, + { 76,504}, + { 77, 12}, + { 78,788}, + { 81,688}, + { 83,400}, + { 85,191}, + { 86,705}, + { 87, 70}, + { 89,807}, + { 90,792}, + { 93,492}, + { 94, 31}, + { 95, 51}, + { 98,234}, + { 99,732}, + {336,774}, +}; + +struct map_priv * map_new_mg(struct map_methods *meth, struct attr **attrs); + +static int map_id; + +static char *file[]={ + [file_border_ply]="border.ply", + [file_bridge_ply]="bridge.ply", + [file_build_ply]="build.ply", + [file_golf_ply]="golf.ply", + [file_height_ply]="height.ply", + [file_natpark_ply]="natpark.ply", + [file_nature_ply]="nature.ply", + [file_other_ply]="other.ply", + [file_rail_ply]="rail.ply", + [file_sea_ply]="sea.ply", + [file_street_bti]="street.bti", + [file_street_str]="street.str", + [file_strname_stn]="strname.stn", + [file_town_twn]="town.twn", + [file_tunnel_ply]="tunnel.ply", + [file_water_ply]="water.ply", + [file_woodland_ply]="woodland.ply", +}; + +int mg_country_from_isonum(int isonum) +{ + int i; + for (i = 0 ; i < sizeof(country_isonums)/sizeof(struct country_isonum) ; i++) + if (country_isonums[i].isonum == isonum) + return country_isonums[i].country; + return 0; +} + +int mg_country_to_isonum(int country) +{ + int i; + for (i = 0 ; i < sizeof(country_isonums)/sizeof(struct country_isonum) ; i++) + if (country_isonums[i].country == country) + return country_isonums[i].isonum; + return 0; +} + +int mg_country_postal_len(int country) +{ + int i; + for (i = 0 ; i < sizeof(country_isonums)/sizeof(struct country_isonum) ; i++) + if (country_isonums[i].country == country) + return country_isonums[i].postal_len; + return 0; +} + +static char *mg_country_postal_prefix(int isonum) +{ + int i; + for (i = 0 ; i < sizeof(country_isonums)/sizeof(struct country_isonum) ; i++) + if (country_isonums[i].isonum == isonum) + return country_isonums[i].postal_prefix; + return NULL; +} + +struct item_range town_ranges[]={ + {type_town_label,type_port_label}, +}; + +struct item_range street_ranges[]={ + {type_street_nopass,type_street_unkn}, +}; + +struct item_range poly_ranges[]={ + {type_border_country,type_water_line}, + {type_street_unkn,type_street_unkn}, + {type_area,type_last}, +}; + + +static int +file_next(struct map_rect_priv *mr) +{ + int debug=0; + + for (;;) { + mr->current_file++; + if (mr->current_file >= file_end) + return 0; + mr->file=mr->m->file[mr->current_file]; + if (! mr->file) + continue; + switch (mr->current_file) { + case file_strname_stn: + continue; + case file_town_twn: + if (mr->cur_sel && !map_selection_contains_item_range(mr->cur_sel, 0, town_ranges, sizeof(town_ranges)/sizeof(struct item_range))) + continue; + break; + case file_street_str: + if (mr->cur_sel && !map_selection_contains_item_range(mr->cur_sel, 0, street_ranges, sizeof(street_ranges)/sizeof(struct item_range))) + continue; + break; + default: + if (mr->cur_sel && !map_selection_contains_item_range(mr->cur_sel, 0, poly_ranges, sizeof(poly_ranges)/sizeof(struct item_range))) + continue; + break; + } + if (debug) + printf("current file: '%s'\n", file[mr->current_file]); + mr->cur_sel=mr->xsel; + if (block_init(mr)) + return 1; + } +} + +static void +map_destroy_mg(struct map_priv *m) +{ + int i; + + printf("mg_map_destroy\n"); + for (i = 0 ; i < file_end ; i++) { + if (m->file[i]) + file_destroy(m->file[i]); + } +} + +extern int block_lin_count,block_idx_count,block_active_count,block_mem,block_active_mem; + +struct map_rect_priv * +map_rect_new_mg(struct map_priv *map, struct map_selection *sel) +{ + struct map_rect_priv *mr; + int i; + + block_lin_count=0; + block_idx_count=0; + block_active_count=0; + block_mem=0; + block_active_mem=0; + mr=g_new0(struct map_rect_priv, 1); + mr->m=map; + mr->xsel=sel; + mr->current_file=-1; + if (sel && sel->next) + for (i=0 ; i < file_end ; i++) + mr->block_hash[i]=g_hash_table_new(g_int_hash,g_int_equal); + file_next(mr); + return mr; +} + + +static struct item * +map_rect_get_item_mg(struct map_rect_priv *mr) +{ + for (;;) { + switch (mr->current_file) { + case file_town_twn: + if (town_get(mr, &mr->town, &mr->item)) + return &mr->item; + break; + case file_border_ply: + case file_bridge_ply: + case file_build_ply: + case file_golf_ply: + /* case file_height_ply: */ + case file_natpark_ply: + case file_nature_ply: + case file_other_ply: + case file_rail_ply: + case file_sea_ply: + /* case file_tunnel_ply: */ + case file_water_ply: + case file_woodland_ply: + if (poly_get(mr, &mr->poly, &mr->item)) + return &mr->item; + break; + case file_street_str: + if (street_get(mr, &mr->street, &mr->item)) + return &mr->item; + break; + case file_end: + return NULL; + default: + break; + } + if (block_next(mr)) + continue; + if (mr->cur_sel->next) { + mr->cur_sel=mr->cur_sel->next; + if (block_init(mr)) + continue; + } + if (file_next(mr)) + continue; + dbg(1,"lin_count %d idx_count %d active_count %d %d kB (%d kB)\n", block_lin_count, block_idx_count, block_active_count, (block_mem+block_active_mem)/1024, block_active_mem/1024); + return NULL; + } +} + +struct item * +map_rect_get_item_byid_mg(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + mr->current_file = (id_hi >> 16) & 0xff; + switch (mr->current_file) { + case file_town_twn: + if (town_get_byid(mr, &mr->town, id_hi, id_lo, &mr->item)) + return &mr->item; + break; + case file_street_str: + if (street_get_byid(mr, &mr->street, id_hi, id_lo, &mr->item)) + return &mr->item; + break; + default: + if (poly_get_byid(mr, &mr->poly, id_hi, id_lo, &mr->item)) + return &mr->item; + break; + } + return NULL; +} + + +void +map_rect_destroy_mg(struct map_rect_priv *mr) +{ + int i; + for (i=0 ; i < file_end ; i++) + if (mr->block_hash[i]) + g_hash_table_destroy(mr->block_hash[i]); + g_free(mr); +} + +static char * +map_search_mg_convert_special(char *str) +{ + char *ret,*c=g_malloc(strlen(str)*2+1); + + ret=c; + for (;;) { + switch ((unsigned char)(*str)) { + case 0xc4: + *c++='A'; + break; + case 0xd6: + *c++='O'; + break; + case 0xdc: + *c++='U'; + break; + case 0xdf: + *c++='s'; + *c++='s'; + break; + case 0xe4: + *c++='a'; + break; + case 0xf6: + *c++='o'; + break; + case 0xfc: + *c++='u'; + break; + default: + dbg(1,"0x%x\n", *str); + *c++=*str; + break; + } + if (! *str) + return ret; + str++; + } +} + +static int +map_search_setup(struct map_rect_priv *mr) +{ + char *prefix; + dbg(1,"%s\n", attr_to_name(mr->search_type)); + switch (mr->search_type) { + case attr_town_postal: + if (mr->search_item.type != type_country_label) { + dbg(0,"wrong parent type %s\n", item_to_name(mr->search_item.type)); + return 0; + } + prefix=mg_country_postal_prefix(mr->search_item.id_lo); + if (! prefix) + return 0; + tree_search_init(mr->m->dirname, "town.b1", &mr->ts, 0); + mr->current_file=file_town_twn; + mr->search_str=g_strdup_printf("%s%s",prefix,mr->search_attr->u.str); + dbg(0,"search_str='%s'\n",mr->search_str); + mr->search_country=mg_country_from_isonum(mr->search_item.id_lo); + break; + case attr_town_name: + if (mr->search_item.type != type_country_label) { + dbg(0,"wrong parent type %s\n", item_to_name(mr->search_item.type)); + return 0; + } + tree_search_init(mr->m->dirname, "town.b2", &mr->ts, 0x1000); + mr->current_file=file_town_twn; + mr->search_str=map_search_mg_convert_special(mr->search_attr->u.str); + mr->search_country=mg_country_from_isonum(mr->search_item.id_lo); + break; + case attr_district_name: + if (mr->search_item.type != type_country_label) { + dbg(0,"wrong parent type %s\n", item_to_name(mr->search_item.type)); + return 0; + } + tree_search_init(mr->m->dirname, "town.b3", &mr->ts, 0x1000); + mr->current_file=file_town_twn; + mr->search_str=map_search_mg_convert_special(mr->search_attr->u.str); + mr->search_country=mg_country_from_isonum(mr->search_item.id_lo); + break; + case attr_street_name: + if (mr->search_item.type != type_town_streets) { + GList *tmp=maps; + struct item *item=NULL; + struct attr attr; + struct map_rect_priv *mr2; + while (tmp) { + mr2=map_rect_new_mg(tmp->data, NULL); + item=map_rect_get_item_byid_mg(mr2, mr->search_item.id_hi, mr->search_item.id_lo); + if (item) + break; + map_rect_destroy_mg(mr2); + tmp=g_list_next(tmp); + } + if (item) { + if (item_attr_get(item, attr_town_streets_item, &attr)) { + mr->search_item=*attr.u.item; + map_rect_destroy_mg(mr2); + } else { + map_rect_destroy_mg(mr2); + return 0; + } + } else { + dbg(0,"wrong parent type %s %p 0x%x 0x%x\n", item_to_name(mr->search_item.type), item, mr->search_item.id_hi, mr->search_item.id_lo); + return 0; + } + } + dbg(1,"street_assoc=0x%x\n", mr->search_item.id_lo); + tree_search_init(mr->m->dirname, "strname.b1", &mr->ts, 0); + mr->current_file=file_strname_stn; + mr->search_str=g_strdup(mr->search_attr->u.str); + break; + case attr_house_number: + if (!map_priv_is(mr->search_item.map, mr->m)) + return 0; + if (!housenumber_search_setup(mr)) { + dbg(0,"failed to search for attr_house_number\n"); + return 0; + } + break; + default: + dbg(0,"unknown search %s\n",attr_to_name(mr->search_type)); + return 0; + } + mr->file=mr->m->file[mr->current_file]; + block_init(mr); + return 1; +} +static void map_search_cleanup(struct map_rect_priv *mr); + +static struct item * map_search_get_item_mg(struct map_search_priv *ms); + +static struct map_search_priv * +map_search_new_mg(struct map_priv *map, struct item *item, struct attr *search, int partial) +{ + struct map_rect_priv *mr=g_new0(struct map_rect_priv, 1); + dbg(1,"searching for %s '%s'\n", attr_to_name(search->type), search->u.str); + dbg(1,"id_lo=0x%x\n", item->id_lo); + dbg(1,"search=%s\n", search->u.str); + mr->m=map; + mr->search_attr=attr_dup(search); + mr->search_type=search->type; + mr->search_item=*item; + mr->search_partial=partial; + if (search->type == attr_town_or_district_name) { + mr->search_type=attr_town_name; + mr->search_type_next=attr_district_name; + } + if (!map_search_setup(mr)) { + dbg(1,"map_search_new_mg failed\n"); + g_free(mr); + return NULL; + } + mr->search_mr_tmp=map_rect_new_mg(map, NULL); + + return (struct map_search_priv *)mr; +} + +static void +map_search_cleanup(struct map_rect_priv *mr) +{ + g_free(mr->search_str); + mr->search_str=NULL; + tree_search_free(&mr->ts); + mr->search_linear=0; + mr->search_p=NULL; + mr->search_blk_count=0; + mr->search_blk_off=NULL; + mr->search_block=0; +} + +static void +map_search_destroy_mg(struct map_search_priv *ms) +{ + struct map_rect_priv *mr=(struct map_rect_priv *)ms; + + dbg(1,"mr=%p\n", mr); + if (! mr) + return; + map_search_cleanup(mr); + if (mr->search_mr_tmp) + map_rect_destroy_mg(mr->search_mr_tmp); + attr_free(mr->search_attr); + g_free(mr); +} + +static struct item * +map_search_get_item_mg(struct map_search_priv *ms) +{ + struct map_rect_priv *mr=(struct map_rect_priv *)ms; + struct item *ret=NULL; + + if (! mr) + return NULL; + switch (mr->search_type) { + case attr_town_postal: + case attr_town_name: + case attr_district_name: + ret=town_search_get_item(mr); + break; + case attr_street_name: + ret=street_search_get_item(mr); + break; + case attr_house_number: + ret=housenumber_search_get_item(mr); + break; + default: + dbg(0,"unknown search %s\n",attr_to_name(mr->search_type)); + break; + } + if (!ret && mr->search_type_next != attr_none) { + mr->search_type=mr->search_type_next; + mr->search_type_next=attr_none; + map_search_cleanup(mr); + map_search_setup(mr); + return map_search_get_item_mg(ms); + } + return ret; +} + +static struct map_methods map_methods_mg = { + projection_mg, + "iso8859-1", + map_destroy_mg, + map_rect_new_mg, + map_rect_destroy_mg, + map_rect_get_item_mg, + map_rect_get_item_byid_mg, + map_search_new_mg, + map_search_destroy_mg, + map_search_get_item_mg, +}; + + +struct map_priv * +map_new_mg(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + int i,maybe_missing; + struct attr *data=attr_search(attrs, NULL, attr_data); + char *filename; + struct file_wordexp *wexp; + char **wexp_data; + + if (! data) + return NULL; + + wexp=file_wordexp_new(data->u.str); + wexp_data=file_wordexp_get_array(wexp); + + *meth=map_methods_mg; + data=attr_search(attrs, NULL, attr_data); + + m=g_new(struct map_priv, 1); + m->id=++map_id; + m->dirname=g_strdup(wexp_data[0]); + file_wordexp_destroy(wexp); + for (i = 0 ; i < file_end ; i++) { + if (file[i]) { + filename=g_strdup_printf("%s/%s", m->dirname, file[i]); + m->file[i]=file_create_caseinsensitive(filename); + if (! m->file[i]) { + maybe_missing=(i == file_border_ply || i == file_height_ply || i == file_sea_ply); + if (! maybe_missing) + dbg(0,"Failed to load %s\n", filename); + } else + file_mmap(m->file[i]); + g_free(filename); + } + } + maps=g_list_append(maps, m); + + return m; +} + +void +plugin_init(void) +{ + plugin_register_map_type("mg", map_new_mg); +} diff --git a/map/mg/mg.h b/map/mg/mg.h new file mode 100644 index 00000000..e6febc21 --- /dev/null +++ b/map/mg/mg.h @@ -0,0 +1,372 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <glib.h> +#include "item.h" +#include "attr.h" +#include "coord.h" +#include "data.h" +#include "projection.h" +#include "map.h" +#include "file.h" + +struct block_data { + struct file *file; +}; + +struct block { +/* int blocks; + int size; + int next; + struct coord_rect r; + int count;*/ + unsigned char p[32]; +}; +static inline int block_get_blocks(struct block * blk) { unsigned char *p = blk->p; return get_u32(&p); } +static inline int block_get_size(struct block * blk) { unsigned char *p = blk->p+4; return get_u32(&p); } +static inline int block_get_next(struct block * blk) { unsigned char *p = blk->p+8; return get_u32(&p); } +static inline int block_get_count(struct block * blk) { unsigned char *p = blk->p+28; return get_u32(&p); } +static inline void block_get_r(struct block * blk, struct coord_rect * r) { unsigned char *p = blk->p+12; r ->lu.x = get_u32(&p); r ->lu.y = get_u32(&p); r ->rl.x = get_u32(&p); r ->rl.y = get_u32(&p); } + +struct item_priv { + int cidx; + int aidx; + unsigned char *cstart,*cp,*cend; + unsigned char *astart,*ap,*aend; + enum attr_type attr_last; + enum attr_type attr_next; + struct item item; +}; + +struct town_priv { + unsigned int id; /*!< Identifier */ + struct coord c; /*!< Coordinates */ + char *name; /*!< Name */ + char *district; /*!< District */ + char *postal_code1; /*!< Postal code */ + unsigned char order; /*!< Order (Importance) */ + unsigned char type; /*!< Type */ + unsigned short country; /*!< Country */ + unsigned int unknown2; /*!< Unknown */ + unsigned char size; /*!< Size of town */ + unsigned int street_assoc; /*!< Association to streets */ + unsigned char unknown3; /*!< Unknown */ + char *postal_code2; /*!< 2nd postal code */ + unsigned int unknown4; /*!< Unknown */ + + int cidx; + int aidx; + enum attr_type attr_next; + char debug[256]; + char postal[32]; + struct item town_attr_item; +}; + +struct poly_priv { + int poly_num; + unsigned char *poly_next; + int subpoly_num; + int subpoly_num_all; + unsigned char *subpoly_next; + unsigned char *subpoly_start; + unsigned char *p; + struct coord c[2]; + char *name; + unsigned char order; + unsigned char type; + unsigned int polys; + unsigned int *count; + unsigned int count_sum; + + int aidx; + enum attr_type attr_next; +}; + +struct street_header { + /*unsigned char order; + int count;*/ + unsigned char p[5]; +} __attribute__((packed)); +static inline unsigned char street_header_get_order(struct street_header * str) { return *str->p; } +static inline int street_header_get_count(struct street_header * str) { unsigned char *p = str->p+1; return get_u32_unal(&p); } + +struct street_type { + /*unsigned char order; + unsigned short country;*/ + unsigned char p[3]; +} __attribute__((packed)); +static inline unsigned char street_type_get_order(struct street_type * str) { return *str->p; } +static inline unsigned short street_type_get_country(struct street_type * str) { unsigned char *p = str->p+1; return get_u16_unal(&p); } + +struct street_header_type { + struct street_header *header; + int type_count; + struct street_type *type; +}; + +struct street_str { + /*int segid; + unsigned char limit;*/ /* 0x03,0x30=One Way,0x33=No Passing */ + /*unsigned char unknown2; + unsigned char unknown3; + unsigned char type; + unsigned int nameid;*/ + unsigned char p[12]; +}; +static inline int street_str_get_segid(struct street_str * str) { unsigned char *p = str->p; return get_u32_unal(&p); } +static inline unsigned char street_str_get_limit(struct street_str * str) { return str->p[4]; } +static inline unsigned char street_str_get_unknown2(struct street_str * str) { return str->p[5]; } +static inline unsigned char street_str_get_unknown3(struct street_str * str) { return str->p[6]; } +static inline unsigned char street_str_get_type(struct street_str * str) { return str->p[7]; } +static inline unsigned int street_str_get_nameid(struct street_str * str) { unsigned char *p = str->p+8; return get_u32_unal(&p); } + +struct street_name_segment { + int segid; + int country; +}; + +struct street_name { + int len; + int country; + int townassoc; + char *name1; + char *name2; + int segment_count; + struct street_name_segment *segments; + int aux_len; + unsigned char *aux_data; + int tmp_len; + unsigned char *tmp_data; +}; + +struct housenumber { + int number; + char *suffix; +}; + +struct street_name_numbers { + int len; + int tag; + int dist; + int country; + struct coord *c; + struct housenumber first; + struct housenumber last; + int segment_count; + struct street_name_segment *segments; + int aux_len; + unsigned char *aux_data; + int tmp_len; + unsigned char *tmp_data; +}; +static inline void street_name_numbers_get_coord(struct street_name_numbers * str, struct coord * c) { + unsigned char *p=(unsigned char *)str->c; + c->x=get_u32_unal(&p); + c->y=get_u32_unal(&p); +} + +struct street_name_number { + int len; + int tag; + struct coord *c; + struct housenumber first; + struct housenumber last; + struct street_name_segment *segment; +}; + + + +struct street_priv { + struct file *name_file; + struct street_header *header; + int type_count; + struct street_type *type; + struct street_str *str; + struct street_str *str_start; + unsigned char *coord_begin; + unsigned char *p; + unsigned char *p_rewind; + unsigned char *end; + unsigned char *next; + int status; + int status_rewind; + struct coord_rect ref; + int bytes; + int more; + int flags; + int housenumber; + int cidx; + struct coord hnc[100]; + struct housenumber hn[100]; + int hn_count; + struct street_name name; + struct street_name_numbers name_numbers; + struct street_name_number name_number; + enum attr_type attr_next; + char debug[256]; + char first_number[32]; + char last_number[32]; + char current_number[32]; + GHashTable *streetname_hash; +}; + +enum file_index { + file_border_ply=0, + file_bridge_ply, + file_build_ply, + file_golf_ply, + file_height_ply, + file_natpark_ply, + file_nature_ply, + file_other_ply, + file_rail_ply, + file_sea_ply, + file_street_bti, + file_street_str, + file_strname_stn, + file_town_twn, + file_tunnel_ply, + file_water_ply, + file_woodland_ply, + file_end, + file_town_twn_alt1, + file_town_twn_alt2, + file_street_str_alt1, + file_street_str_alt2, + file_street_str_alt3, + file_street_str_alt4, +}; + +struct map_priv { + int id; + struct file *file[file_end]; + char *dirname; +}; + +#define BT_STACK_SIZE 32 + +struct block_bt_priv { + struct block *b; + struct coord_rect r, r_curr; + int next; + int block_count; + struct coord_rect stack[BT_STACK_SIZE]; + int stackp; + int order; + unsigned char *p; + unsigned char *end; +}; + +struct block_priv { + int block_num; + struct coord_rect b_rect; + unsigned char *block_start; + struct block *b; + unsigned char *p; + unsigned char *end; + unsigned char *p_start; + int binarytree; + struct block_bt_priv bt; +}; + +struct block_offset { +/* unsigned short offset; + unsigned short block;*/ + unsigned char p[4]; +}; +static inline unsigned short block_offset_get_offset(struct block_offset * blk) { unsigned char *p = blk->p; return get_u16_unal(&p); } +static inline unsigned short block_offset_get_block(struct block_offset * blk) { unsigned char *p = blk->p+2; return get_u16_unal(&p); } + +struct tree_search_node { + struct tree_hdr *hdr; + unsigned char *p; + unsigned char *last; + unsigned char *end; + int low; + int high; + int last_low; + int last_high; + }; + +struct tree_search { + struct file *f; + int last_node; + int curr_node; + struct tree_search_node nodes[5]; +}; + + +struct map_rect_priv { + struct map_selection *xsel; + struct map_selection *cur_sel; + + struct map_priv *m; + enum file_index current_file; + struct file *file; + struct block_priv b; + struct item item; + struct town_priv town; + struct poly_priv poly; + struct street_priv street; + struct tree_search ts; + int search_country; + struct item search_item; + struct attr *search_attr; + char *search_str; + int search_partial; + int search_linear; + unsigned char *search_p; + int search_blk_count; + enum attr_type search_type,search_type_next; + struct map_rect_priv *search_mr_tmp; + struct item *search_item_tmp; + struct block_offset *search_blk_off; + int search_block; + GHashTable *block_hash[file_end]; + struct item_priv item3; +}; + +int mg_country_from_isonum(int isonum); +int mg_country_to_isonum(int country); +int mg_country_postal_len(int country); + +int block_init(struct map_rect_priv *mr); +int block_next(struct map_rect_priv *mr); +int block_get_byindex(struct file *file, int idx, struct block_priv *blk); +int block_next_lin(struct map_rect_priv *mr); + +int tree_search_hv(char *dirname, char *filename, unsigned int search1, unsigned int search2, int *result); +int town_get(struct map_rect_priv *mr, struct town_priv *poly, struct item *item); +int town_get_byid(struct map_rect_priv *mr, struct town_priv *twn, int id_hi, int id_lo, struct item *item); +struct item * town_search_get_item(struct map_rect_priv *mr); +int poly_get(struct map_rect_priv *mr, struct poly_priv *poly, struct item *item); +int poly_get_byid(struct map_rect_priv *mr, struct poly_priv *poly, int id_hi, int id_lo, struct item *item); +int street_get(struct map_rect_priv *mr, struct street_priv *street, struct item *item); +int street_get_byid(struct map_rect_priv *mr, struct street_priv *street, int id_hi, int id_lo, struct item *item); +int street_name_get_byid(struct map_rect_priv *mr, struct street_priv *street, int id_hi, int id_lo, struct item *item); +struct item * street_search_get_item(struct map_rect_priv *mr); +void tree_search_init(char *dirname, char *filename, struct tree_search *ts, int offset); +void tree_search_free(struct tree_search *ts); +int tree_search_next(struct tree_search *ts, unsigned char **p, int dir); +int tree_search_next_lin(struct tree_search *ts, unsigned char **p); +struct item * housenumber_search_get_item(struct map_rect_priv *mr); +struct map_rect_priv * map_rect_new_mg(struct map_priv *map, struct map_selection *sel); +void map_rect_destroy_mg(struct map_rect_priv *mr); +struct item *map_rect_get_item_byid_mg(struct map_rect_priv *mr, int id_hi, int id_lo); +int housenumber_search_setup(struct map_rect_priv *mr); diff --git a/map/mg/poly.c b/map/mg/poly.c new file mode 100644 index 00000000..5bd08303 --- /dev/null +++ b/map/mg/poly.c @@ -0,0 +1,265 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <string.h> +#include "debug.h" +#include "mg.h" + +static void +poly_coord_rewind(void *priv_data) +{ + struct poly_priv *poly=priv_data; + + poly->p=poly->subpoly_start; + +} + +static int +poly_coord_get(void *priv_data, struct coord *c, int count) +{ + struct poly_priv *poly=priv_data; + int ret=0; + + while (count--) { + if (poly->p >= poly->subpoly_next) + break; + c->x=get_u32_unal(&poly->p); + c->y=get_u32_unal(&poly->p); + c++; + ret++; + } + return ret; +} + +static void +poly_attr_rewind(void *priv_data) +{ + struct poly_priv *poly=priv_data; + + poly->aidx=0; +} + +static int +poly_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct poly_priv *poly=priv_data; + + attr->type=attr_type; + switch (attr_type) { + case attr_any: + while (poly->attr_next != attr_none) { + if (poly_attr_get(poly, poly->attr_next, attr)) + return 1; + } + return 0; + case attr_label: + attr->u.str=poly->name; + poly->attr_next=attr_none; + if (attr->u.str[0]) + return 1; + return 0; + default: + return 0; + } + return 1; +} + +static struct item_methods poly_meth = { + poly_coord_rewind, + poly_coord_get, + poly_attr_rewind, + poly_attr_get, +}; + +static void +poly_get_data(struct poly_priv *poly, unsigned char **p) +{ + poly->c[0].x=get_u32_unal(p); + poly->c[0].y=get_u32_unal(p); + poly->c[1].x=get_u32_unal(p); + poly->c[1].y=get_u32_unal(p); + *p+=sizeof(struct coord); + poly->name=(char *)(*p); + while (**p) { + (*p)++; + } + (*p)++; + poly->order=*(*p)++; + poly->type=*(*p)++; + poly->polys=get_u32_unal(p); + poly->count=(unsigned int *)(*p); (*p)+=poly->polys*sizeof(unsigned int); + poly->count_sum=get_u32_unal(p); +} + +int +poly_get(struct map_rect_priv *mr, struct poly_priv *poly, struct item *item) +{ + struct coord_rect r; + + for (;;) { + if (mr->b.p >= mr->b.end) + return 0; + if (mr->b.p == mr->b.p_start) { + poly->poly_num=0; + poly->subpoly_num=0; + poly->subpoly_num_all=0; + poly->poly_next=mr->b.p; + item->meth=&poly_meth; + } + if (poly->poly_num >= block_get_count(mr->b.b)) + return 0; + if (!poly->subpoly_num) { + mr->b.p=poly->poly_next; + item->id_lo=mr->b.p-mr->file->begin; + poly_get_data(poly, &mr->b.p); + poly->poly_next=mr->b.p+poly->count_sum*sizeof(struct coord); + poly->poly_num++; + r.lu=poly->c[0]; + r.rl=poly->c[1]; + if (mr->cur_sel && (poly->order > mr->cur_sel->order*3 || !coord_rect_overlap(&mr->cur_sel->u.c_rect, &r))) { + poly->subpoly_num_all+=poly->polys; + mr->b.p=poly->poly_next; + continue; + } + switch(poly->type) { + case 0x13: + item->type=type_poly_wood; + break; + case 0x14: + item->type=type_poly_town; + break; + case 0x15: + item->type=type_poly_cemetery; + break; + case 0x16: + item->type=type_poly_building; + break; + case 0x17: + item->type=type_poly_museum; + break; + case 0x19: + item->type=type_poly_place; + break; + case 0x1b: + item->type=type_poly_commercial_center; + break; + case 0x1e: + item->type=type_poly_industry; + break; + case 0x23: + /* FIXME: what is this ?*/ + item->type=type_poly_place; + break; + case 0x24: + item->type=type_poly_car_parking; + break; + case 0x28: + item->type=type_poly_airport; + break; + case 0x29: + item->type=type_poly_station; + break; + case 0x2d: + item->type=type_poly_hospital; + break; + case 0x2e: + item->type=type_poly_hospital; + break; + case 0x2f: + item->type=type_poly_university; + break; + case 0x30: + item->type=type_poly_university; + break; + case 0x32: + item->type=type_poly_park; + break; + case 0x34: + item->type=type_poly_sport; + break; + case 0x35: + item->type=type_poly_sport; + break; + case 0x37: + item->type=type_poly_golf_course; + break; + case 0x38: + item->type=type_poly_national_park; + break; + case 0x39: + item->type=type_poly_nature_park; + break; + case 0x3c: + item->type=type_poly_water; + break; + case 0xbc: + item->type=type_water_line; + break; + case 0xc3: + /* FIXME: what is this ?*/ + item->type=type_border_state; + break; + case 0xc6: + item->type=type_border_country; + break; + case 0xc7: + item->type=type_border_state; + break; + case 0xd0: + item->type=type_rail; + break; + default: + dbg(0,"Unknown poly type 0x%x '%s' 0x%x,0x%x\n", poly->type,poly->name,r.lu.x,r.lu.y); + item->type=type_street_unkn; + } + if (!map_selection_contains_item(mr->cur_sel, 0, item->type)) { + poly->subpoly_num_all+=poly->polys; + mr->b.p=poly->poly_next; + continue; + } + } else + mr->b.p=poly->subpoly_next; + dbg(1,"%d %d %s\n", poly->subpoly_num_all, mr->b.block_num, poly->name); + item->id_lo=poly->subpoly_num_all | (mr->b.block_num << 16); + item->id_hi=(mr->current_file << 16); + dbg(1,"0x%x 0x%x\n", item->id_lo, item->id_hi); + poly->subpoly_next=mr->b.p+L(poly->count[poly->subpoly_num])*sizeof(struct coord); + poly->subpoly_num++; + poly->subpoly_num_all++; + if (poly->subpoly_num >= poly->polys) + poly->subpoly_num=0; + poly->subpoly_start=poly->p=mr->b.p; + item->priv_data=poly; + poly->attr_next=attr_label; + return 1; + } +} + +int +poly_get_byid(struct map_rect_priv *mr, struct poly_priv *poly, int id_hi, int id_lo, struct item *item) +{ + int count=id_lo & 0xffff; + int ret=0; + block_get_byindex(mr->m->file[mr->current_file], id_lo >> 16, &mr->b); + while (count-- >= 0) { + ret=poly_get(mr, poly, item); + } + return ret; +} + diff --git a/map/mg/street.c b/map/mg/street.c new file mode 100644 index 00000000..8260dc56 --- /dev/null +++ b/map/mg/street.c @@ -0,0 +1,1389 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "debug.h" +#include "mg.h" + +int coord_debug; + +#if 0 +static void street_name_numbers_get(struct street_name_numbers *name_numbers, unsigned char **p); +static void street_name_number_get(struct street_name_number *name_number, unsigned char **p); + +static void +street_name_debug(struct street_name *sn, FILE *out) +{ + struct street_name_numbers nns; + unsigned char *p=sn->aux_data; + unsigned char *end=p+sn->aux_len; + int i; + + while (p < end) { + unsigned char *pn,*pn_end; + struct street_name_number nn; + street_name_numbers_get(&nns, &p); + fprintf(out,"0x%x 0x%x type=town_label label=\"%s(%d):0x%x:%d%s-%d%s\" debug=\"len=0x%x\"",nns.c->x,nns.c->y,sn->name2, sn->segment_count, nns.tag, nns.first.number,nns.first.suffix,nns.last.number,nns.last.suffix,nns.len); + for (i = 0 ; i < sn->segment_count ; i++) { + fprintf(out," debug=\"segment(%d)=0x%x\"",i,sn->segments[i].segid); + } + fprintf(out,"\n"); + pn=nns.aux_data; + pn_end=nns.aux_data+nns.aux_len; + while (pn < pn_end) { + street_name_number_get(&nn, &pn); + fprintf(out,"0x%x 0x%x type=town_label label=\"%s:0x%x:%d%s-%d%s\" debug=\"len=0x%x\"\n", nn.c->x, nn.c->y, sn->name2, nn.tag, nn.first.number, nn.first.suffix, nn.last.number,nn.last.suffix,nn.len); + } + } + fflush(out); +} +#endif + +static void +street_name_get(struct street_name *name, unsigned char **p) +{ +#if 0 + static FILE *out; + static GHashTable *hash; +#endif + unsigned char *start=*p; + name->len=get_u16_unal(p); + name->country=get_u16_unal(p); + name->townassoc=get_u32_unal(p); + name->name1=get_string(p); + name->name2=get_string(p); + name->segment_count=get_u32_unal(p); + name->segments=(struct street_name_segment *)(*p); + (*p)+=(sizeof (struct street_name_segment))*name->segment_count; + name->aux_len=name->len-(*p-start); + name->aux_data=*p; + name->tmp_len=name->aux_len; + name->tmp_data=name->aux_data; + *p=start+name->len; +#if 0 + if (! out) { + out=fopen("hn.txt","a"); + } + if (! hash) { + hash=g_hash_table_new(NULL,NULL); + } + if (! g_hash_table_lookup(hash, *p)) { + g_hash_table_insert(hash, *p, (void *)1); + street_name_debug(name, out); + } +#endif +} + +static int +street_name_eod(struct street_name *name) +{ + return (name->tmp_data >= name->aux_data+name->aux_len); +} + +static void +street_name_numbers_get(struct street_name_numbers *name_numbers, unsigned char **p) +{ + unsigned char *start=*p; + name_numbers->len=get_u16_unal(p); + name_numbers->tag=get_u8(p); + name_numbers->dist=get_u32_unal(p); + name_numbers->country=get_u32_unal(p); + name_numbers->c=coord_get(p); + name_numbers->first.number=get_u16_unal(p); + name_numbers->first.suffix=get_string(p); + name_numbers->last.number=get_u16_unal(p); + name_numbers->last.suffix=get_string(p); + name_numbers->segment_count=get_u32_unal(p); + name_numbers->segments=(struct street_name_segment *)(*p); + (*p)+=sizeof(struct street_name_segment)*name_numbers->segment_count; + name_numbers->aux_len=name_numbers->len-(*p-start); + name_numbers->aux_data=*p; + name_numbers->tmp_len=name_numbers->aux_len; + name_numbers->tmp_data=name_numbers->aux_data; + *p=start+name_numbers->len; +} + +static int +street_name_numbers_eod(struct street_name_numbers *name_numbers) +{ + return (name_numbers->tmp_data >= name_numbers->aux_data+name_numbers->aux_len); +} + + +static int +street_name_numbers_get_byid(struct street_name_numbers *name_numbers, struct street_name *name, int id) +{ + unsigned char *p=name->aux_data; + unsigned char *end=p+name->aux_len; + while (id >= 0) { + if (p >= end) + return 0; + street_name_numbers_get(name_numbers, &p); + id--; + } + return 1; +} + +static void +street_name_number_get(struct street_name_number *name_number, unsigned char **p) +{ + unsigned char *start=*p; + name_number->len=get_u16_unal(p); + name_number->tag=get_u8(p); + name_number->c=coord_get(p); + name_number->first.number=get_u16_unal(p); + name_number->first.suffix=get_string(p); + name_number->last.number=get_u16_unal(p); + name_number->last.suffix=get_string(p); + name_number->segment=(struct street_name_segment *)p; + *p=start+name_number->len; +} + +static int +street_name_number_get_byid(struct street_name_number *name_number, struct street_name_numbers *name_numbers, int id) +{ + unsigned char *p=name_numbers->tmp_data; + unsigned char *end=p+name_numbers->tmp_len; + while (id >= 0) { + if (p >= end) + return 0; + street_name_number_get(name_number, &p); + id--; + } + return 1; +} + +static void +street_name_get_by_id(struct street_name *name, struct file *file, unsigned long id) +{ + unsigned char *p; + if (id) { + p=file->begin+id+0x2000; + street_name_get(name, &p); + } +} + +static int street_get_bytes(struct coord_rect *r) +{ + int bytes,dx,dy; + bytes=2; + dx=r->rl.x-r->lu.x; + dy=r->lu.y-r->rl.y; + dbg_assert(dx > 0); + dbg_assert(dy > 0); + if (dx > 32767 || dy > 32767) + bytes=3; + if (dx > 8388608 || dy > 8388608) + bytes=4; + + return bytes; +} + +static int street_get_coord(unsigned char **pos, int bytes, struct coord_rect *ref, struct coord *f) +{ + unsigned char *p; + int x,y,flags=0; + + p=*pos; + x=*p++; + x|=(*p++) << 8; + if (bytes == 2) { + if ( x > 0x7fff) { + x=0x10000-x; + flags=1; + } + } + else if (bytes == 3) { + x|=(*p++) << 16; + if ( x > 0x7fffff) { + x=0x1000000-x; + flags=1; + } + } else { + x|=(*p++) << 16; + x|=(*p++) << 24; + if (x < 0) { + x=-x; + flags=1; + } + } + y=*p++; + y|=(*p++) << 8; + if (bytes == 3) { + y|=(*p++) << 16; + } else if (bytes == 4) { + y|=(*p++) << 16; + y|=(*p++) << 24; + } + if (f) { + f->x=ref->lu.x+x; + f->y=ref->rl.y+y; + dbg(1,"0x%x,0x%x + 0x%x,0x%x = 0x%x,0x%x\n", x, y, ref->lu.x, ref->rl.y, f->x, f->y); + } + *pos=p; + return flags; +} + +static void +street_coord_get_begin(unsigned char **p) +{ + struct street_str *str; + + str=(struct street_str *)(*p); + while (street_str_get_segid(str)) { + str++; + } + (*p)=(unsigned char *)str; + (*p)+=4; +} + + +static void +street_coord_rewind(void *priv_data) +{ + struct street_priv *street=priv_data; + + street->p=street->next=NULL; + street->status=street->status_rewind; +} + +static int +street_coord_get_helper(struct street_priv *street, struct coord *c) +{ + unsigned char *n; + if (street->p+street->bytes*2 >= street->end) + return 0; + if (street->status >= 4) + return 0; + n=street->p; + if (street_get_coord(&street->p, street->bytes, &street->ref, c)) { + if (street->status) + street->next=n; + street->status+=2; + if (street->status == 5) + return 0; + } + return 1; +} + +static int +street_coord_get(void *priv_data, struct coord *c, int count) +{ + struct street_priv *street=priv_data; + int ret=0,i,scount; +#ifdef DEBUG_COORD_GET + int segid,debug=0; +#endif + + if (! street->p && count) { + street->p=street->coord_begin; + scount=street->str-street->str_start; + for (i = 0 ; i < scount ; i++) { + street->status=street_str_get_segid(&street->str[i+1]) >= 0 ? 0:1; + while (street_coord_get_helper(street, c)); + street->p=street->next; + } + street->status_rewind=street->status=street_str_get_segid(&street->str[1]) >= 0 ? 0:1; + } +#ifdef DEBUG_COORD_GET + segid=street_str_get_segid(&street->str[0]); + if (segid < 0) + segid=-segid; + if (segid == 0x15) + debug=1; + if (debug) { + dbg(0,"enter 0x%x\n",segid); + } +#endif + while (count > 0) { + if (street_coord_get_helper(street, c)) { +#ifdef DEBUG_COORD_GET + if (debug) { + dbg(0,"0x%x,0x%x\n", c->x, c->y); + } +#endif + c++; + ret++; + count--; + } else { + street->more=0; + return ret; + } + } + return ret; +} + + static void +street_attr_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static int +street_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct street_priv *street=priv_data; + int nameid; + + dbg(1,"segid 0x%x\n", street_str_get_segid(street->str)); + attr->type=attr_type; + switch (attr_type) { + case attr_any: + while (street->attr_next != attr_none) { + if (street_attr_get(street, street->attr_next, attr)) + return 1; + } + return 0; + case attr_label: + street->attr_next=attr_street_name; + nameid=street_str_get_nameid(street->str); + if (! nameid) + return 0; + if (! street->name.len) + street_name_get_by_id(&street->name,street->name_file,nameid); + attr->u.str=street->name.name2; + if (attr->u.str && attr->u.str[0]) + return 1; + attr->u.str=street->name.name1; + if (attr->u.str && attr->u.str[0]) + return 1; + return 0; + case attr_street_name: + street->attr_next=attr_street_name_systematic; + nameid=street_str_get_nameid(street->str); + if (! nameid) + return 0; + if (! street->name.len) + street_name_get_by_id(&street->name,street->name_file,nameid); + attr->u.str=street->name.name2; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_street_name_systematic: + street->attr_next=attr_flags; + nameid=street_str_get_nameid(street->str); + if (! nameid) + return 0; + if (! street->name.len) + street_name_get_by_id(&street->name,street->name_file,nameid); + attr->u.str=street->name.name1; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_flags: + attr->u.num=street->flags; + street->attr_next=attr_country_id; + return 1; + case attr_country_id: + street->attr_next=attr_debug; + nameid=street_str_get_nameid(street->str); + if (! nameid) + return 0; + if (! street->name.len) + street_name_get_by_id(&street->name,street->name_file,nameid); + attr->u.num=mg_country_to_isonum(street->name.country); + return 1; + case attr_debug: + street->attr_next=attr_none; + { + struct street_str *str=street->str; + sprintf(street->debug,"order:0x%x\nsegid:0x%x\nlimit:0x%x\nunknown2:0x%x\nunknown3:0x%x\ntype:0x%x\nnameid:0x%x\ntownassoc:0x%x",street_header_get_order(street->header),street_str_get_segid(str),street_str_get_limit(str),street_str_get_unknown2(str),street_str_get_unknown3(str),street_str_get_type(str),street_str_get_nameid(str), street->name.len ? street->name.townassoc : 0); + attr->u.str=street->debug; + } + return 1; + default: + return 0; + } + return 1; +} + +static struct item_methods street_meth = { + street_coord_rewind, + street_coord_get, + street_attr_rewind, + street_attr_get, +}; + +static void +street_get_data(struct street_priv *street, unsigned char **p) +{ + street->header=(struct street_header *)(*p); + (*p)+=sizeof(struct street_header); + street->type_count=street_header_get_count(street->header); + street->type=(struct street_type *)(*p); + (*p)+=street->type_count*sizeof(struct street_type); +} + + +static void +street_housenumber_coord_rewind(void *priv_data) +{ + struct street_priv *street=priv_data; + street->cidx=0; +} + +static void +street_housenumber_attr_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static int +street_housenumber_coord_get(void *priv_data, struct coord *c, int count) +{ + struct street_priv *street=priv_data; + if (street->cidx || !count || !street->name.len) + return 0; + *c=street->hnc[street->housenumber-2]; + street->cidx=1; + return 1; +} + +static int +street_housenumber_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct street_priv *street=priv_data; + struct housenumber *hn; + attr->type=attr_type; + switch (attr_type) { + case attr_label: + hn=&street->hn[street->housenumber-2]; + sprintf(street->debug,"%d%s",hn->number,hn->suffix); + attr->u.str=street->debug; + return 1; + default: + dbg(0,"unknown item\n"); + return 0; + } +} + +static struct item_methods street_housenumber_meth = { + street_housenumber_coord_rewind, + street_housenumber_coord_get, + street_housenumber_attr_rewind, + street_housenumber_attr_get, +}; + +static void +project(struct coord *c, int count, int di, int dlr, struct coord *ch) +{ + struct coord cr; + int dx,dy; + int l; + + if (di > 0) { + dx=c[1].x-c[0].x; + dy=c[1].y-c[0].y; + cr=c[0]; + } else if (di < 0) { + dx=c[count-1].x-c[count-2].x; + dy=c[count-1].y-c[count-2].y; + cr=c[count-1]; + } else { + dx=c[1].x-c[0].x; + dy=c[1].y-c[0].y; + di=0; + if (count % 2) { + cr=c[count/2]; + } else { + cr.x=(c[count/2-1].x+c[count/2].x)/2; + cr.y=(c[count/2-1].y+c[count/2].y)/2; + } + } + l=sqrtf(dx*dx+dy*dy); + if (!l) { + *ch=cr; + return; + } + ch->x=cr.x+(di*dx+dlr*dy)/l; + ch->y=cr.y+(di*dy-dlr*dx)/l; +} + +static int +street_lookup_housenumber(struct street_priv *street) +{ + int i,count,scount,maxcount=16384; + struct coord c[maxcount]; + struct street_name_numbers nns; + unsigned char *p=street->name.aux_data; + unsigned char *end=p+street->name.aux_len; + unsigned char *pn,*pn_end; + + street->hn_count=0; + street_coord_rewind(street); + scount=street_coord_get(street, c, maxcount/2); + if (scount >= maxcount/2) { + dbg(0,"overflow"); + } + for (i = 0 ; i < scount-1 ; i++) { + c[scount+i].x=(c[i].x+c[i+1].x)/2; + c[scount+i].y=(c[i].y+c[i+1].y)/2; + } + count=scount*2-1; + while (p < end) { + struct street_name_number nn; + street_name_numbers_get(&nns, &p); + pn=nns.aux_data; + pn_end=nns.aux_data+nns.aux_len; + while (pn < pn_end) { + street_name_number_get(&nn, &pn); + for (i = 0 ; i < count ; i++) { + int dx=nn.c->x-c[i].x; + int dy=nn.c->y-c[i].y; + int dlr,dir; + if (dx < 3 && dx > -3 && dy < 3 && dy > -3) { + dir=15; + dlr=15; + switch (nn.tag & 0xf) { + case 0xa: + break; + case 0xb: + dlr=-dlr; + dir=-dir; + break; + case 0xe: + dlr=-dlr; + break; + case 0xf: + dir=-dir; + break; + default: + dbg(0,"unknown tag 0x%x\n",nn.tag); +#if 0 + continue; +#endif + } + if (street_str_get_type(street->str) & 0x40) { + dir=-dir; + dlr=-dlr; + } + if (nn.first.number == nn.last.number && !strcmp(nn.first.suffix, nn.last.suffix)) + dir=0; + project(c, scount, dir, dlr, &street->hnc[street->hn_count]); + street->hn[street->hn_count]=nn.first; + street->hn_count++; + g_assert(street->hn_count < 100); + project(c, scount, -dir, dlr, &street->hnc[street->hn_count]); + street->hn[street->hn_count]=nn.last; + street->hn_count++; + g_assert(street->hn_count < 100); + dbg(1,"found %d%s %d%s\n",nn.first.number,nn.first.suffix,nn.last.number,nn.last.suffix); + } + } + } + } + return 1; +} + +static int +street_get_housenumber(struct map_rect_priv *mr, struct street_priv *street, struct item *item) +{ + int nameid; + nameid=street_str_get_nameid(street->str); + if (! nameid) + return 0; + if (! street->name.len) + street_name_get_by_id(&street->name,street->name_file,nameid); + if (! street->name.aux_len) + return 0; + if (!street->hn_count) + street_lookup_housenumber(street); + if (street->housenumber > street->hn_count) + return 0; + item->type=type_town_label; + item->id_hi = (item->id_hi & 0xffffff) | (street->housenumber*0x10000000+0x1000000); + item->meth=&street_housenumber_meth; + street->cidx=0; + street->housenumber++; + return 1; +} + + /*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 */ +static unsigned char limit[]={0,0,1,1,1,2,2,4,6,6,12,13,14,20,20,20,20,20,20}; + +int +street_get(struct map_rect_priv *mr, struct street_priv *street, struct item *item) +{ + int *flags; + struct coord_rect r; + for (;;) { + while (street->more) { + struct coord c; + street_coord_get(street, &c, 1); + } +#if 0 + if (street->housenumber) { + if (street_get_housenumber(mr, street, item)) + return 1; + street->housenumber=0; + } +#endif + if (mr->b.p == mr->b.p_start) { + street_get_data(street, &mr->b.p); + street->name_file=mr->m->file[file_strname_stn]; + if (mr->cur_sel && street_header_get_order(street->header) > limit[mr->cur_sel->order]) + return 0; + street->end=mr->b.end; + block_get_r(mr->b.b, &r); + street->ref=r; + street->bytes=street_get_bytes(&r); + street->str_start=street->str=(struct street_str *)mr->b.p; + street->coord_begin=mr->b.p; + street_coord_get_begin(&street->coord_begin); + street->p=street->coord_begin; + street->type--; + item->meth=&street_meth; + item->priv_data=street; + } else { + street->str++; + street->p=street->next; + } + if (! street_str_get_segid(street->str)) + return 0; + if (street_str_get_segid(street->str) < 0) + street->type++; +#if 0 + dbg_assert(street->p != NULL); +#endif + street->next=NULL; + street->status_rewind=street->status=street_str_get_segid(&street->str[1]) >= 0 ? 0:1; +#if 0 + if (street->type->country != 0x31) { + printf("country=0x%x\n", street->type->country); + } +#endif + item->id_hi=street_type_get_country(street->type) | (mr->current_file << 16); + item->id_lo=street_str_get_segid(street->str) > 0 ? street_str_get_segid(street->str) : -street_str_get_segid(street->str); + switch(street_str_get_type(street->str) & 0x1f) { + case 0xf: /* very small street */ + if (street_str_get_limit(street->str) == 0x33) + item->type=type_street_nopass; + else + item->type=type_street_0; + break; + case 0xd: + item->type=type_ferry; + break; + case 0xc: /* small street */ + item->type=type_street_1_city; + break; + case 0xb: + item->type=type_street_2_city; + break; + case 0xa: + if ((street_str_get_limit(street->str) == 0x03 || street_str_get_limit(street->str) == 0x30) && street_header_get_order(street->header) < 4) + item->type=type_street_4_city; + else + item->type=type_street_3_city; + break; + case 0x9: + if (street_header_get_order(street->header) < 5) + item->type=type_street_4_city; + else if (street_header_get_order(street->header) < 7) + item->type=type_street_2_city; + else + item->type=type_street_1_city; + break; + case 0x8: + item->type=type_street_2_land; + break; + case 0x7: + if ((street_str_get_limit(street->str) == 0x03 || street_str_get_limit(street->str) == 0x30) && street_header_get_order(street->header) < 4) + item->type=type_street_4_city; + else + item->type=type_street_3_land; + break; + case 0x6: + item->type=type_ramp; + break; + case 0x5: + item->type=type_street_4_land; + break; + case 0x4: + item->type=type_street_4_land; + break; + case 0x3: + item->type=type_street_n_lanes; + break; + case 0x2: + item->type=type_highway_city; + break; + case 0x1: + item->type=type_highway_land; + break; + default: + item->type=type_street_unkn; + dbg(0,"unknown type 0x%x\n",street_str_get_type(street->str)); + } + flags=item_get_default_flags(item->type); + if (flags) + street->flags=*flags; + else + street->flags=0; + if (street_str_get_type(street->str) & 0x40) { + street->flags|=(street_str_get_limit(street->str) & 0x30) ? AF_ONEWAYREV:0; + street->flags|=(street_str_get_limit(street->str) & 0x03) ? AF_ONEWAY:0; + } else { + street->flags|=(street_str_get_limit(street->str) & 0x30) ? AF_ONEWAY:0; + street->flags|=(street_str_get_limit(street->str) & 0x03) ? AF_ONEWAYREV:0; + } +#if 0 + coord_debug=(street->str->unknown2 != 0x40 || street->str->unknown3 != 0x40); + if (coord_debug) { + item->type=type_street_unkn; + printf("%d %02x %02x %02x %02x\n", street->str->segid, street->str->type, street->str->limit, street->str->unknown2, street->str->unknown3); + } +#endif + street->p_rewind=street->p; + street->name.len=0; + street->attr_next=attr_label; + street->more=1; + street->housenumber=1; + street->hn_count=0; + if (!map_selection_contains_item(mr->cur_sel, 0, item->type)) + continue; + item->meth=&street_meth; + item->priv_data=street; + return 1; + } +} + +int +street_get_byid(struct map_rect_priv *mr, struct street_priv *street, int id_hi, int id_lo, struct item *item) +{ + int country=id_hi & 0xffff; + int res; + struct coord_rect r; + dbg(1,"enter(%p,%p,0x%x,0x%x,%p)\n", mr, street, id_hi, id_lo, item); + if (! country) + return 0; + tree_search_hv(mr->m->dirname, "street", (id_lo >> 8) | (country << 24), id_lo & 0xff, &res); + dbg(1,"res=0x%x (blk=0x%x)\n", res, res >> 12); + block_get_byindex(mr->m->file[mr->current_file], res >> 12, &mr->b); + street_get_data(street, &mr->b.p); + street->name_file=mr->m->file[file_strname_stn]; + street->end=mr->b.end; + block_get_r(mr->b.b, &r); + street->ref=r; + street->bytes=street_get_bytes(&r); + street->str_start=street->str=(struct street_str *)mr->b.p; + street->coord_begin=mr->b.p; + street_coord_get_begin(&street->coord_begin); + street->p=street->coord_begin; + street->type--; + item->meth=&street_meth; + item->priv_data=street; + street->str+=(res & 0xfff)-1; + dbg(1,"segid 0x%x\n", street_str_get_segid(&street->str[1])); + return street_get(mr, street, item); +#if 0 + mr->b.p=mr->b.block_start+(res & 0xffff); + return town_get(mr, twn, item); +#endif + + return 0; +} + +struct street_name_index { + int block; + unsigned short country; + int town_assoc; + char name[0]; +} __attribute__((packed)); + +static unsigned char +latin1_tolower(unsigned char c) +{ + if (c >= 'A' && c <= 'Z') + return c - 'A' + 'a'; + if (c == 0xc4 || c == 0xc9 || c == 0xd6 || c == 0xdc) + return c+0x20; + return c; +} + +static unsigned char +latin1_tolower_ascii(unsigned char c) +{ + unsigned char ret=latin1_tolower(c); + switch (ret) { + case 0xe4: + return 'a'; + case 0xe9: + return 'e'; + case 0xf6: + return 'o'; + case 0xfc: + return 'u'; + default: + if (ret >= 0x80) + dbg(1,"ret=0x%x\n",c); + return ret; + } +} + +static int +strncasecmp_latin1(char *str1, char *str2, int len) +{ + int d; + while (len--) { + d=latin1_tolower((unsigned char)(*str1))-latin1_tolower((unsigned char)(*str2)); + if (d) + return d; + if (! *str1) + return 0; + str1++; + str2++; + } + return 0; +} + +static int +strncasecmp_latin1_ascii(char *str1, char *str2, int len) +{ + int d; + while (len--) { + d=latin1_tolower_ascii((unsigned char)(*str1))-latin1_tolower_ascii((unsigned char)(*str2)); + if (d) + return d; + if (! *str1) + return 0; + str1++; + str2++; + } + return 0; +} + +static int +street_search_compare_do(struct map_rect_priv *mr, int country, int town_assoc, char *name) +{ + int d,len; + + dbg(1,"enter"); + dbg(1,"country 0x%x town_assoc 0x%x name '%s'\n", country, town_assoc, name); + d=(mr->search_item.id_hi & 0xffff)-country; + dbg(1,"country %d (%d vs %d)\n", d, mr->search_item.id_hi & 0xffff, country); + if (!d) { + if (mr->search_item.id_lo == town_assoc ) { + dbg(1,"town_assoc match (0x%x)\n", town_assoc); + len=mr->search_partial ? strlen(mr->search_str):INT_MAX; + d=strncasecmp_latin1(mr->search_str, name, len); + if (!strncasecmp_latin1_ascii(mr->search_str, name, len)) + d=0; + dbg(1,"string %d\n", d); + } else { + if (town_assoc < mr->search_item.id_lo) + d=1; + else + d=-1; + dbg(1,"assoc %d 0x%x-0x%x\n",d, mr->search_item.id_lo, town_assoc); + } + } + dbg(1,"d=%d\n", d); + return d; +} + +static int +street_search_compare(unsigned char **p, struct map_rect_priv *mr) +{ + struct street_name_index *i; + int ret; + + dbg(1,"enter\n"); + i=(struct street_name_index *)(*p); + *p+=sizeof(*i)+strlen(i->name)+1; + + dbg(1,"block 0x%x\n", i->block); + + ret=street_search_compare_do(mr, i->country, i->town_assoc, i->name); + if (ret <= 0) + mr->search_block=i->block; + return ret; +} + +static void +street_name_numbers_coord_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static void +street_name_numbers_attr_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static int +street_name_numbers_coord_get(void *priv_data, struct coord *c, int count) +{ + return 0; +} + +static int +street_name_numbers_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + attr->type=attr_type; + switch (attr_type) { + default: + dbg(0,"unknown item\n"); + return 0; + } +} + + + + + +static struct item_methods street_name_numbers_meth = { + street_name_numbers_coord_rewind, + street_name_numbers_coord_get, + street_name_numbers_attr_rewind, + street_name_numbers_attr_get, +}; + + +static void +street_name_coord_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static void +street_name_attr_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static int +street_name_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + struct street_name_numbers snns; + unsigned char *p=mr->street.name.aux_data; + + dbg(1,"aux_data=%p\n", p); + if (count) { + street_name_numbers_get(&snns, &p); + street_name_numbers_get_coord(&snns, c); + return 1; + } + + return 0; +} + +#if 0 +static void +debug(struct map_rect_priv *mr) +{ + int i; + struct street_name_numbers nns; + unsigned char *p=mr->street.name.aux_data; + unsigned char *end=p+mr->street.name.aux_len; + printf("len=0x%x\n", mr->street.name.aux_len); + for (i = 0 ; i < mr->street.name.aux_len ; i++) { + printf("%02x ",mr->street.name.aux_data[i]); + } + printf("\n"); + { + while (p < end) { + unsigned char *pn,*pn_end; + struct street_name_number nn; + street_name_numbers_get(&nns, &p); + printf("name_numbers:\n"); + printf(" len 0x%x\n", nns.len); + printf(" tag 0x%x\n", nns.tag); + printf(" dist 0x%x\n", nns.dist); + printf(" country 0x%x\n", nns.country); + printf(" coord 0x%x,0x%x\n", nns.c->x, nns.c->y); + printf(" first %d\n", nns.first.number); + printf(" last %d\n", nns.last.number); + printf(" segment count 0x%x\n", nns.segment_count); + printf(" aux_len 0x%x\n", nns.aux_len); + pn=nns.aux_data; + pn_end=nns.aux_data+nns.aux_len; + while (pn < pn_end) { + printf(" number:\n"); + street_name_number_get(&nn, &pn); + printf(" len 0x%x\n", nn.len); + printf(" tag 0x%x\n", nn.tag); + printf(" coord 0x%x,0x%x\n", nn.c->x, nn.c->y); + printf(" first %d\n", nn.first.number); + printf(" last %d\n", nn.last.number); + } + } + } +} +#endif + +static int +street_name_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + + attr->type=attr_type; + switch (attr_type) { + case attr_street_name: + attr->u.str=mr->street.name.name2; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_street_name_systematic: + attr->u.str=mr->street.name.name1; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_town_name: + case attr_district_name: + case attr_postal: + if (!mr->search_item_tmp) + mr->search_item_tmp=map_rect_get_item_byid_mg(mr->search_mr_tmp, mr->street.name_numbers.country | (file_town_twn << 16), mr->street.name_numbers.dist); + if (!mr->search_item_tmp) + return 0; + return item_attr_get(mr->search_item_tmp, attr_type, attr); + default: + dbg(0,"%p\n",mr->street.name_numbers.dist); + dbg(0,"unknown attr %s\n",attr_to_name(attr_type)); + return 0; + } +} + + + + + +static struct item_methods street_name_meth = { + street_name_coord_rewind, + street_name_coord_get, + street_name_attr_rewind, + street_name_attr_get, +}; + + +int +street_name_get_byid(struct map_rect_priv *mr, struct street_priv *street, int id_hi, int id_lo, struct item *item) +{ + mr->current_file=id_hi >> 16; + street->name_file=mr->m->file[mr->current_file]; + item->type=type_street_name; + item->id_hi=id_hi; + item->id_lo=id_lo; + item->meth=&street_name_meth; + item->map=NULL; + item->priv_data=mr; + mr->b.p=street->name_file->begin+item->id_lo; + dbg(1,"last %p map %p file %d begin %p\n", mr->b.p, mr->m, mr->current_file, mr->m->file[mr->current_file]->begin); + street_name_get(&street->name, &mr->b.p); + return 1; +} + +static struct item * +street_search_get_item_street_name(struct map_rect_priv *mr) +{ + int dir=1,leaf; + unsigned char *last; + + dbg(1,"enter\n"); + if (! mr->search_blk_count) { + dbg(1,"partial 0x%x '%s' ***\n", mr->town.street_assoc, mr->search_str); + if (mr->search_linear) + return NULL; + dbg(1,"tree_search_next\n"); + mr->search_block=-1; + while ((leaf=tree_search_next(&mr->ts, &mr->search_p, dir)) != -1) { + dir=street_search_compare(&mr->search_p, mr); + } + dbg(1,"dir=%d mr->search_block=0x%x\n", dir, mr->search_block); + if (mr->search_block == -1) + return NULL; + mr->search_blk_count=1; + block_get_byindex(mr->m->file[file_strname_stn], mr->search_block, &mr->b); + mr->b.p=mr->b.block_start+12; + } + dbg(1,"name id 0x%x\n", mr->b.p-mr->m->file[file_strname_stn]->begin); + if (! mr->search_blk_count) + return NULL; + for (;;) { + if (mr->b.p >= mr->b.end) { + if (!block_next_lin(mr)) { + dbg(1,"end of blocks in %p, %p\n", mr->m->file[file_strname_stn]->begin, mr->m->file[file_strname_stn]->end); + return NULL; + } + mr->b.p=mr->b.block_start+12; + } + while (mr->b.p < mr->b.end) { + last=mr->b.p; + street_name_get(&mr->street.name, &mr->b.p); + dir=street_search_compare_do(mr, mr->street.name.country, mr->street.name.townassoc, mr->street.name.name2); + dbg(1,"country 0x%x assoc 0x%x name1 '%s' name2 '%s' dir=%d\n", mr->street.name.country, mr->street.name.townassoc, mr->street.name.name1, mr->street.name.name2, dir); + if (dir < 0) { + dbg(1,"end of data\n"); + mr->search_blk_count=0; + return NULL; + } + if (!dir) { + dbg(1,"result country 0x%x assoc 0x%x name1 '%s' name2 '%s' dir=%d aux_data=%p len=0x%x\n", mr->street.name.country, mr->street.name.townassoc, mr->street.name.name1, mr->street.name.name2, dir, mr->street.name.aux_data, mr->street.name.aux_len); + mr->item.type = type_street_name; + mr->item.id_hi=(file_strname_stn << 16); + mr->item.id_lo=last-mr->m->file[file_strname_stn]->begin; + dbg(1,"id 0x%x 0x%x last %p map %p file %d begin %p\n", mr->item.id_hi, mr->item.id_lo, last, mr->m, mr->current_file, mr->m->file[mr->current_file]->begin); + mr->item.meth=&street_name_meth; + mr->item.map=NULL; + mr->item.priv_data=mr; + /* debug(mr); */ + dbg(1,"last %p\n",last); + return &mr->item; + } + } + } +} + +static void +district_debug(struct map_rect_priv *mr, int country, int dist) +{ + struct map_rect_priv *mrt; + struct item *item; + struct attr attrn; + + mrt=map_rect_new_mg(mr->m, NULL); + item=map_rect_get_item_byid_mg(mrt, country | (file_town_twn << 16), dist); + dbg(0,"item=%p\n",item); + if (item_attr_get(item, attr_town_postal, &attrn)) + dbg(0,"postal=%s\n",attrn.u.str); + if (item_attr_get(item, attr_town_name, &attrn)) + dbg(0,"town_name=%s\n",attrn.u.str); + if (item_attr_get(item, attr_district_name, &attrn)) + dbg(0,"district_name=%s\n",attrn.u.str); + map_rect_destroy_mg(mrt); +} + +static int +street_name_numbers_get_next(struct map_rect_priv *mr, struct street_name *name, char *start, char **p, int mode, int *id, struct street_name_numbers *ret) +{ + struct street_name_numbers tmp; + char *ps,*pt; + int found; + while (*p < name->aux_data+name->aux_len) { + ps=*p; + street_name_numbers_get(ret, p); + (*id)++; + found=0; + pt=name->aux_data; + while (pt < ps) { + street_name_numbers_get(&tmp, &pt); + if (tmp.country == ret->country && tmp.dist == ret->dist) { + found=1; + break; + } + } + if (!found) { + dbg(0,"district 0x%x\n",ret->dist); + district_debug(mr, ret->country, ret->dist); + return 1; + } + } + return 0; +} + + +static struct item * +street_search_get_item_street_name_district(struct map_rect_priv *mr, int flag) +{ + if (street_name_eod(&mr->street.name)) + return NULL; + if (!street_name_numbers_get_next(mr, &mr->street.name, NULL, &mr->street.name.tmp_data, 1, &mr->item.id_hi, &mr->street.name_numbers)) + return NULL; + return &mr->item; +} + +struct item * +street_search_get_item(struct map_rect_priv *mr) +{ + struct item *item; + for (;;) { + if (!mr->street.name.tmp_data || street_name_eod(&mr->street.name)) { + item=street_search_get_item_street_name(mr); + if (!item) + return NULL; + if (!mr->street.name.aux_len) + return item; + } + mr->item.id_hi++; + street_name_numbers_get(&mr->street.name_numbers, &mr->street.name.tmp_data); + mr->search_item_tmp=NULL; + return &mr->item; + } +} + +static int +street_name_numbers_next(struct map_rect_priv *mr) +{ + if (street_name_eod(&mr->street.name)) + return 0; + dbg(1,"%p vs %p\n",mr->street.name.tmp_data, mr->street.name.aux_data); + street_name_numbers_get(&mr->street.name_numbers, &mr->street.name.tmp_data); + return 1; +} + +static int +street_name_number_next(struct map_rect_priv *mr) +{ + if (street_name_numbers_eod(&mr->street.name_numbers)) + return 0; + street_name_number_get(&mr->street.name_number, &mr->street.name_numbers.tmp_data); + sprintf(mr->street.first_number,"%d%s",mr->street.name_number.first.number,mr->street.name_number.first.suffix); + sprintf(mr->street.last_number,"%d%s",mr->street.name_number.last.number,mr->street.name_number.last.suffix); + mr->street.current_number[0]='\0'; + return 1; +} + +static void +housenumber_coord_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static void +housenumber_attr_rewind(void *priv_data) +{ + /* struct street_priv *street=priv_data; */ + +} + +static int +housenumber_coord_get(void *priv_data, struct coord *c, int count) +{ + return 0; +} + +static int +housenumber_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + attr->type=attr_type; + switch (attr_type) { + case attr_house_number: + attr->u.str=mr->street.current_number; + return 1; + case attr_town_name: + case attr_district_name: + case attr_postal: + if (!mr->search_item_tmp) + mr->search_item_tmp=map_rect_get_item_byid_mg(mr->search_mr_tmp, mr->street.name_numbers.country | (file_town_twn << 16), mr->street.name_numbers.dist); + if (!mr->search_item_tmp) + return 0; + return item_attr_get(mr->search_item_tmp, attr_type, attr); + default: + dbg(0,"unknown attr %s\n",attr_to_name(attr_type)); + return 0; + } +} + + +static struct item_methods housenumber_meth = { + housenumber_coord_rewind, + housenumber_coord_get, + housenumber_attr_rewind, + housenumber_attr_get, +}; + +int +housenumber_search_setup(struct map_rect_priv *mr) +{ + dbg(1,"enter (0x%x,0x%x)\n",mr->search_item.id_hi,mr->search_item.id_lo); + int id=mr->search_item.id_hi & 0xff; + mr->current_file=file_strname_stn; + mr->street.name_file=mr->m->file[mr->current_file]; + mr->b.p=mr->street.name_file->begin+mr->search_item.id_lo; + mr->search_str=g_strdup(mr->search_attr->u.str); + dbg(1,"last %p\n",mr->b.p); + street_name_get(&mr->street.name, &mr->b.p); +#if 0 + debug(mr); +#endif + while (id > 0) { + id--; + dbg(1,"loop\n"); + if (!street_name_numbers_next(mr)) + return 0; + } + mr->item.type=type_house_number; + mr->item.priv_data=mr; + mr->item.id_hi=mr->search_item.id_hi + 0x100; + mr->item.meth=&housenumber_meth; + if (!id) + mr->item.id_hi+=1; + mr->item.id_lo=mr->search_item.id_lo; + dbg(1,"getting name_number %p vs %p + %d\n",mr->street.name_numbers.tmp_data,mr->street.name_numbers.aux_data, mr->street.name_numbers.aux_len); + if (!street_name_number_next(mr)) + return 0; + dbg(1,"enter\n"); + // debug(mr); + return 1; +} + +static int +house_number_next(char *number, char *first, char *last, int interpolation, int *percentage) +{ + int firstn=atoi(first); + int lastn=atoi(last); + int current,delta,len=lastn-firstn; + if (interpolation) { + len/=2; + } + if (!number[0]) { + strcpy(number,first); + delta=0; + } else { + current=atoi(number)+(interpolation ? 2:1); + if (current > lastn) + return 0; + sprintf(number,"%d",current); + delta=current-firstn; + } + if (percentage) { + if (len) + *percentage=delta*100/len; + else + *percentage=50; + } + return 1; +} + +struct item * +housenumber_search_get_item(struct map_rect_priv *mr) +{ + int d; + dbg(1,"enter %s %s\n",mr->street.first_number,mr->street.last_number); + for (;;) { + if (!house_number_next(mr->street.current_number, mr->street.first_number, mr->street.last_number, 0, NULL)) { + if (!street_name_number_next(mr)) + return NULL; + continue; + } + if (mr->search_partial) + d=strncasecmp(mr->search_str, mr->street.current_number, strlen(mr->search_str)); + else + d=strcasecmp(mr->search_str, mr->street.current_number); + if (!d) { + mr->search_item_tmp=NULL; + return &mr->item; + } + } +} diff --git a/map/mg/town.c b/map/mg/town.c new file mode 100644 index 00000000..cee04683 --- /dev/null +++ b/map/mg/town.c @@ -0,0 +1,286 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdio.h> +#include <string.h> +#include "debug.h" +#include "mg.h" + + + +static void +town_coord_rewind(void *priv_data) +{ + struct town_priv *twn=priv_data; + + twn->cidx=0; +} + +static int +town_coord_get(void *priv_data, struct coord *c, int count) +{ + struct town_priv *twn=priv_data; + + if (twn->cidx || count <= 0) + return 0; + twn->cidx=1; + *c=twn->c; + return 1; +} + +static void +town_attr_rewind(void *priv_data) +{ + struct town_priv *twn=priv_data; + + twn->aidx=0; + twn->attr_next=attr_label; +} + +static int +town_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct town_priv *twn=priv_data; + int len; + + attr->type=attr_type; + switch (attr_type) { + case attr_any: + while (twn->attr_next != attr_none) { + if (town_attr_get(twn, twn->attr_next, attr)) + return 1; + } + return 0; + case attr_label: + attr->u.str=twn->district; + twn->attr_next=attr_town_name; + if (attr->u.str[0]) + return 1; + attr->u.str=twn->name; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_town_name: + attr->u.str=twn->name; + twn->attr_next=attr_town_postal; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_town_postal: + case attr_postal: + strncpy(twn->postal, twn->postal_code1, 32); + attr->u.str=twn->postal; + len=mg_country_postal_len(twn->country); + if (!len) + len=31; + twn->postal[len]='\0'; + twn->attr_next=attr_district_name; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_district_name: + attr->u.str=twn->district; + twn->attr_next=attr_debug; + return ((attr->u.str && attr->u.str[0]) ? 1:0); + case attr_town_streets_item: + twn->town_attr_item.type=type_town_streets; + twn->town_attr_item.id_hi=twn->country | (file_town_twn << 16) | 0x10000000; + twn->town_attr_item.id_lo=twn->street_assoc; + attr->u.item=&twn->town_attr_item; + twn->attr_next=attr_debug; + return 1; + case attr_debug: + sprintf(twn->debug, "order %d\nsize %d\nstreet_assoc 0x%x", twn->order, twn->size, twn->street_assoc); + attr->u.str=twn->debug; + twn->attr_next=attr_none; + return 1; + default: + dbg_assert(1==0); + return 0; + } + return 1; +} + +static struct item_methods town_meth = { + town_coord_rewind, + town_coord_get, + town_attr_rewind, + town_attr_get, +}; + +static void +town_get_data(struct town_priv *twn, unsigned char **p) +{ + twn->id=get_u32_unal(p); + twn->c.x=get_u32_unal(p); + twn->c.y=get_u32_unal(p); + twn->name=get_string(p); + twn->district=get_string(p); + twn->postal_code1=get_string(p); + twn->order=get_u8(p); /* 1-15 (19) */ + twn->country=get_u16_unal(p); + twn->type=get_u8(p); + twn->unknown2=get_u32_unal(p); + twn->size=get_u8(p); + twn->street_assoc=get_u32_unal(p); + twn->unknown3=get_u8(p); + twn->postal_code2=get_string(p); + twn->unknown4=get_u32_unal(p); +#if 0 + printf("%s\t%s\t%s\t%d\t%d\t%d\n",twn->name,twn->district,twn->postal_code1,twn->order, twn->country, twn->type); +#endif +} + /*0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 */ +static unsigned char limit[]={0,1,2,2,4,6,8,10,11,13,14,14,14,20,20,20,20,20,20}; + +static enum item_type town_item[]={type_town_label_5e1, type_town_label_1e2, type_town_label_2e2, type_town_label_5e2, type_town_label_1e3, type_town_label_1e3, type_town_label_2e3, type_town_label_5e3, type_town_label_1e4, type_town_label_2e4, type_town_label_5e4, type_town_label_1e5, type_town_label_1e5, type_town_label_2e5, type_town_label_5e5, type_town_label_1e6, type_town_label_2e6}; +static enum item_type district_item[]={type_district_label_5e1, type_district_label_1e2, type_district_label_2e2, type_district_label_5e2, type_district_label_1e3, type_district_label_1e3, type_district_label_2e3, type_district_label_5e3, type_district_label_1e4, type_district_label_2e4, type_district_label_5e4, type_district_label_1e5, type_district_label_1e5, type_district_label_2e5, type_district_label_5e5, type_district_label_1e6, type_district_label_2e6}; +int +town_get(struct map_rect_priv *mr, struct town_priv *twn, struct item *item) +{ + int size; + for (;;) { + if (mr->b.p >= mr->b.end) + return 0; + town_get_data(twn, &mr->b.p); + twn->cidx=0; + twn->aidx=0; + twn->attr_next=attr_label; + if (! mr->cur_sel || (twn->order <= limit[mr->cur_sel->order] && coord_rect_contains(&mr->cur_sel->u.c_rect,&twn->c))) { + switch(twn->type) { + case 1: + size=twn->size; + if (size >= sizeof(town_item)/sizeof(enum item_type)) + size=sizeof(town_item)/sizeof(enum item_type)-1; + item->type=town_item[size]; + break; + case 3: + size=twn->size; + if (size == 6 && twn->order < 14) + size++; + if (size == 5 && twn->order < 14) + size+=2; + if (size >= sizeof(district_item)/sizeof(enum item_type)) + size=sizeof(district_item)/sizeof(enum item_type)-1; + item->type=district_item[size]; + break; + case 4: + item->type=type_port_label; + break; + case 9: + item->type=type_highway_exit_label; + break; + default: + printf("unknown town type 0x%x '%s' '%s' 0x%x,0x%x\n", twn->type, twn->name, twn->district, twn->c.x, twn->c.y); + item->type=type_town_label; + } + if (map_selection_contains_item(mr->cur_sel, 0, item->type)) { + item->id_hi=twn->country | (mr->current_file << 16); + item->id_lo=twn->id; + item->priv_data=twn; + item->meth=&town_meth; + return 1; + } + } + } +} + +int +town_get_byid(struct map_rect_priv *mr, struct town_priv *twn, int id_hi, int id_lo, struct item *item) +{ + int country=id_hi & 0xffff; + int res; + if (!tree_search_hv(mr->m->dirname, "town", (id_lo >> 8) | (country << 24), id_lo & 0xff, &res)) + return 0; + block_get_byindex(mr->m->file[mr->current_file], res >> 16, &mr->b); + mr->b.p=mr->b.block_start+(res & 0xffff); + return town_get(mr, twn, item); +} + +static int +town_search_compare(unsigned char **p, struct map_rect_priv *mr) +{ + int country, d; + char *name; + + if (mr->search_type == attr_town_postal) { + mr->search_blk_count=1; + mr->search_blk_off=(struct block_offset *)(*p); + *p+=4; + name=get_string(p); + d=0; + } else { + country=get_u16_unal(p); + dbg(1,"country 0x%x ", country); + name=get_string(p); + dbg(1,"name '%s' ",name); + mr->search_blk_count=get_u32_unal(p); + mr->search_blk_off=(struct block_offset *)(*p); + dbg(1,"len %d ", mr->search_blk_count); + (*p)+=mr->search_blk_count*4; + d=mr->search_country-country; + } + if (!d) { + if (mr->search_partial) + d=strncasecmp(mr->search_str, name, strlen(mr->search_str)); + else + d=strcasecmp(mr->search_str, name); + } + dbg(1,"%d \n",d); + return d; + +} + + + +struct item * +town_search_get_item(struct map_rect_priv *mr) +{ + int dir=1,leaf; + + if (! mr->search_blk_count) { + dbg(1,"partial %d 0x%x '%s' ***\n", mr->search_partial, mr->search_country, mr->search_str); + if (! mr->search_linear) { + while ((leaf=tree_search_next(&mr->ts, &mr->search_p, dir)) != -1) { + dir=town_search_compare(&mr->search_p, mr); + if (! dir) { + mr->search_linear=1; + mr->search_p=NULL; + break; + } + } + if (! mr->search_linear) { + dbg(1,"not found\n"); + return NULL; + } + } + if (! tree_search_next_lin(&mr->ts, &mr->search_p)) { + dbg(1,"linear not found\n"); + return NULL; + } + if (town_search_compare(&mr->search_p, mr)) { + dbg(1,"no match\n"); + return NULL; + } + dbg(1,"found %d blocks\n",mr->search_blk_count); + } + if (! mr->search_blk_count) + return NULL; + dbg(1,"block 0x%x offset 0x%x\n", block_offset_get_block(mr->search_blk_off), block_offset_get_offset(mr->search_blk_off)); + block_get_byindex(mr->m->file[mr->current_file], block_offset_get_block(mr->search_blk_off), &mr->b); + mr->b.p=mr->b.block_start+block_offset_get_offset(mr->search_blk_off); + town_get(mr, &mr->town, &mr->item); + mr->search_blk_off++; + mr->search_blk_count--; + return &mr->item; +} diff --git a/map/mg/tree.c b/map/mg/tree.c new file mode 100644 index 00000000..9c2bb08e --- /dev/null +++ b/map/mg/tree.c @@ -0,0 +1,282 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdio.h> +#include <string.h> +#include "debug.h" +#include "mg.h" + +struct tree_hdr { + /*unsigned int addr; + unsigned int size; + unsigned int low;*/ + unsigned char p[12]; +}; +static inline unsigned int tree_hdr_get_addr(struct tree_hdr * tree) { unsigned char *p = tree->p; return get_u32(&p); } +static inline unsigned int tree_hdr_get_size(struct tree_hdr * tree) { unsigned char *p = tree->p+4; return get_u32(&p); } +static inline unsigned int tree_hdr_get_low(struct tree_hdr * tree) { unsigned char *p = tree->p+8; return get_u32(&p); } + +struct tree_hdr_h { +/* unsigned int addr; + unsigned int size;*/ + unsigned char p[8]; +}; +static inline unsigned int tree_hdr_h_get_addr(struct tree_hdr_h * tree) { unsigned char *p = tree->p; return get_u32(&p); } +static inline unsigned int tree_hdr_h_get_size(struct tree_hdr_h * tree) { unsigned char *p = tree->p+4; return get_u32(&p); } + +struct tree_leaf_h { +/* unsigned int lower; + unsigned int higher; + unsigned int match; + unsigned int value;*/ + unsigned char p[16]; +}; +static inline unsigned int tree_leaf_h_get_lower(struct tree_leaf_h * tree) { unsigned char *p = tree->p; return get_u32(&p); } +static inline unsigned int tree_leaf_h_get_higher(struct tree_leaf_h * tree) { unsigned char *p = tree->p+4; return get_u32(&p); } +static inline unsigned int tree_leaf_h_get_match(struct tree_leaf_h * tree) { unsigned char *p = tree->p+8; return get_u32(&p); } +static inline unsigned int tree_leaf_h_get_value(struct tree_leaf_h * tree) { unsigned char *p = tree->p+12; return get_u32(&p); } + + +struct tree_hdr_v { + /*unsigned int count; + unsigned int next; + unsigned int unknown;*/ + unsigned char p[12]; +}; +static inline unsigned int tree_hdr_v_get_count(struct tree_hdr_v * tree) { unsigned char *p = tree->p; return get_u32_unal(&p); } +static inline unsigned int tree_hdr_v_get_next(struct tree_hdr_v * tree) { unsigned char *p = tree->p+4; return get_u32_unal(&p); } +static inline unsigned int tree_hdr_v_get_unknown(struct tree_hdr_v * tree) { unsigned char *p = tree->p+8; return get_u32_unal(&p); } + +struct tree_leaf_v { + unsigned char key; + /*int value;*/ + unsigned char p[4]; +} __attribute__((packed)); +static inline int tree_leaf_v_get_value(struct tree_leaf_v * tree) { unsigned char *p = tree->p; return get_u32_unal(&p); } + +static int +tree_search_h(struct file *file, unsigned int search) +{ + unsigned char *p=file->begin,*end; + int last,i=0,value,lower; + struct tree_hdr_h *thdr; + struct tree_leaf_h *tleaf; + + dbg(1,"enter\n"); + while (i++ < 1000) { + thdr=(struct tree_hdr_h *)p; + p+=sizeof(*thdr); + end=p+tree_hdr_h_get_size(thdr); + dbg(1,"@0x%x\n", p-file->begin); + last=0; + while (p < end) { + tleaf=(struct tree_leaf_h *)p; + p+=sizeof(*tleaf); + dbg(1,"low:0x%x high:0x%x match:0x%x val:0x%x search:0x%x\n", tree_leaf_h_get_lower(tleaf), tree_leaf_h_get_higher(tleaf), tree_leaf_h_get_match(tleaf), tree_leaf_h_get_value(tleaf), search); + value=tree_leaf_h_get_value(tleaf); + if (value == search) + return tree_leaf_h_get_match(tleaf); + if (value > search) { + dbg(1,"lower\n"); + lower=tree_leaf_h_get_lower(tleaf); + if (lower) + last=lower; + break; + } + last=tree_leaf_h_get_higher(tleaf); + } + if (! last || last == -1) + return 0; + p=file->begin+last; + } + return 0; +} + +static int +tree_search_v(struct file *file, int offset, int search) +{ + unsigned char *p=file->begin+offset; + int i=0,count,next; + struct tree_hdr_v *thdr; + struct tree_leaf_v *tleaf; + while (i++ < 1000) { + thdr=(struct tree_hdr_v *)p; + p+=sizeof(*thdr); + count=tree_hdr_v_get_count(thdr); + dbg(1,"offset=0x%x count=0x%x\n", p-file->begin, count); + while (count--) { + tleaf=(struct tree_leaf_v *)p; + p+=sizeof(*tleaf); + dbg(1,"0x%x 0x%x\n", tleaf->key, search); + if (tleaf->key == search) + return tree_leaf_v_get_value(tleaf); + } + next=tree_hdr_v_get_next(thdr); + if (! next) + break; + p=file->begin+next; + } + return 0; +} + +int +tree_search_hv(char *dirname, char *filename, unsigned int search_h, unsigned int search_v, int *result) +{ + struct file *f_idx_h, *f_idx_v; + char buffer[4096]; + int h,v; + + dbg(1,"enter(%s, %s, 0x%x, 0x%x, %p)\n",dirname, filename, search_h, search_v, result); + sprintf(buffer, "%s/%s.h1", dirname, filename); + f_idx_h=file_create_caseinsensitive(buffer); + if (! f_idx_h) + return 0; + file_mmap(f_idx_h); + sprintf(buffer, "%s/%s.v1", dirname, filename); + f_idx_v=file_create_caseinsensitive(buffer); + dbg(1,"%p %p\n", f_idx_h, f_idx_v); + if (! f_idx_v) { + file_destroy(f_idx_h); + return 0; + } + file_mmap(f_idx_v); + if ((h=tree_search_h(f_idx_h, search_h))) { + dbg(1,"h=0x%x\n", h); + if ((v=tree_search_v(f_idx_v, h, search_v))) { + dbg(1,"v=0x%x\n", v); + *result=v; + file_destroy(f_idx_v); + file_destroy(f_idx_h); + dbg(1,"return 1\n"); + return 1; + } + } + file_destroy(f_idx_v); + file_destroy(f_idx_h); + dbg(1,"return 0\n"); + return 0; +} + +static struct tree_search_node * +tree_search_enter(struct tree_search *ts, int offset) +{ + struct tree_search_node *tsn=&ts->nodes[++ts->curr_node]; + unsigned char *p; + p=ts->f->begin+offset; + tsn->hdr=(struct tree_hdr *)p; + tsn->p=p+sizeof(struct tree_hdr); + tsn->last=tsn->p; + tsn->end=p+tree_hdr_get_size(tsn->hdr); + tsn->low=tree_hdr_get_low(tsn->hdr); + tsn->high=tree_hdr_get_low(tsn->hdr); + dbg(1,"pos 0x%x addr 0x%x size 0x%x low 0x%x end 0x%x\n", p-ts->f->begin, tree_hdr_get_addr(tsn->hdr), tree_hdr_get_size(tsn->hdr), tree_hdr_get_low(tsn->hdr), tsn->end-ts->f->begin); + return tsn; +} + +int tree_search_next(struct tree_search *ts, unsigned char **p, int dir) +{ + struct tree_search_node *tsn=&ts->nodes[ts->curr_node]; + + if (! *p) + *p=tsn->p; + dbg(1,"next *p=%p dir=%d\n", *p, dir); + dbg(1,"low1=0x%x high1=0x%x\n", tsn->low, tsn->high); + if (dir <= 0) { + dbg(1,"down 0x%x\n", tsn->low); + if (tsn->low != 0xffffffff) { + tsn=tree_search_enter(ts, tsn->low); + *p=tsn->p; + tsn->high=get_u32(p); + ts->last_node=ts->curr_node; + dbg(1,"saving last2 %d 0x%x\n", ts->curr_node, tsn->last-ts->f->begin); + dbg(1,"high2=0x%x\n", tsn->high); + return 0; + } + return -1; + } + tsn->low=tsn->high; + tsn->last=*p; + tsn->high=get_u32_unal(p); + dbg(1,"saving last3 %d %p\n", ts->curr_node, tsn->last); + if (*p < tsn->end) + return (tsn->low == 0xffffffff ? 1 : 0); + dbg(1,"end reached high=0x%x\n",tsn->high); + if (tsn->low != 0xffffffff) { + dbg(1,"low 0x%x\n", tsn->low); + tsn=tree_search_enter(ts, tsn->low); + *p=tsn->p; + tsn->high=get_u32_unal(p); + ts->last_node=ts->curr_node; + dbg(1,"saving last4 %d 0x%x\n", ts->curr_node, tsn->last-ts->f->begin); + dbg(1,"high4=0x%x\n", tsn->high); + return 0; + } + return -1; +} + +int tree_search_next_lin(struct tree_search *ts, unsigned char **p) +{ + struct tree_search_node *tsn=&ts->nodes[ts->curr_node]; + int high; + + dbg(1,"pos=%d 0x%x\n", ts->curr_node, *p-ts->f->begin); + if (*p) + ts->nodes[ts->last_node].last=*p; + *p=tsn->last; + for (;;) { + high=get_u32_unal(p); + if (*p < tsn->end) { + ts->last_node=ts->curr_node; + while (high != 0xffffffff) { + tsn=tree_search_enter(ts, high); + dbg(1,"reload %d\n",ts->curr_node); + high=tsn->low; + } + return 1; + } + dbg(1,"eon %d 0x%x 0x%x\n", ts->curr_node, *p-ts->f->begin, tsn->end-ts->f->begin); + if (! ts->curr_node) + break; + ts->curr_node--; + tsn=&ts->nodes[ts->curr_node]; + *p=tsn->last; + } + + return 0; +} + +void +tree_search_init(char *dirname, char *filename, struct tree_search *ts, int offset) +{ + char buffer[4096]; + sprintf(buffer, "%s/%s", dirname, filename); + ts->f=file_create_caseinsensitive(buffer); + ts->curr_node=-1; + if (ts->f) { + file_mmap(ts->f); + tree_search_enter(ts, offset); + } +} + +void +tree_search_free(struct tree_search *ts) +{ + if (ts->f) + file_destroy(ts->f); +} diff --git a/map/poi_geodownload/Makefile.am b/map/poi_geodownload/Makefile.am new file mode 100644 index 00000000..e30f359e --- /dev/null +++ b/map/poi_geodownload/Makefile.am @@ -0,0 +1,7 @@ +SUBDIRS=libmdb +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -I$(srcdir)/libmdb/include -DMODULE=map_poi_geodownload +modulemap_LTLIBRARIES = libmap_poi_geodownload.la +libmap_poi_geodownload_la_SOURCES = poi_geodownload.c +libmap_poi_geodownload_la_LIBADD = -Llibmdb -lmdb +libmap_poi_geodownload_la_LDFLAGS = -module -avoid-version diff --git a/map/poi_geodownload/libmdb/Makefile.am b/map/poi_geodownload/libmdb/Makefile.am new file mode 100644 index 00000000..e2e440ba --- /dev/null +++ b/map/poi_geodownload/libmdb/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS=include +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(srcdir)/include +noinst_LTLIBRARIES = libmdb.la +libmdb_la_SOURCES=backend.c catalog.c data.c dump.c file.c iconv.c index.c kkd.c like.c map.c mem.c money.c options.c props.c sargs.c stats.c table.c worktable.c write.c diff --git a/map/poi_geodownload/libmdb/backend.c b/map/poi_geodownload/libmdb/backend.c new file mode 100644 index 00000000..902805c3 --- /dev/null +++ b/map/poi_geodownload/libmdb/backend.c @@ -0,0 +1,301 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* +** functions to deal with different backend database engines +*/ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static int is_init; +static GHashTable *mdb_backends; + + /* Access data types */ +static MdbBackendType mdb_access_types[] = { + {"Unknown 0x00", 0,0,0 }, + {"Boolean", 0,0,0}, + {"Byte", 0,0,0}, + {"Integer", 0,0,0}, + {"Long Integer", 0,0,0}, + {"Currency", 0,0,0}, + {"Single", 0,0,0}, + {"Double", 0,0,0}, + {"DateTime (Short)", 0,0,1}, + {"Unknown 0x09", 0,0,0}, + {"Text", 1,0,1}, + {"OLE", 1,0,1}, + {"Memo/Hyperlink",1,0,1}, + {"Unknown 0x0d",0,0,0}, + {"Unknown 0x0e",0,0,0}, + {"Replication ID",0,0,0}, + {"Numeric",1,1,0} +}; + +/* Oracle data types */ +static MdbBackendType mdb_oracle_types[] = { + {"Oracle_Unknown 0x00",0,0,0}, + {"NUMBER",1,0,0}, + {"NUMBER",1,0,0}, + {"NUMBER",1,0,0}, + {"NUMBER",1,0,0}, + {"NUMBER",1,0,0}, + {"FLOAT",0,0,0}, + {"FLOAT",0,0,0}, + {"DATE",0,0,0}, + {"Oracle_Unknown 0x09",0,0,0}, + {"VARCHAR2",1,0,1}, + {"BLOB",1,0,1}, + {"CLOB",1,0,1}, + {"Oracle_Unknown 0x0d",0,0,0}, + {"Oracle_Unknown 0x0e",0,0,0}, + {"NUMBER",1,0,0}, + {"NUMBER",1,0,0}, +}; + +/* Sybase/MSSQL data types */ +static MdbBackendType mdb_sybase_types[] = { + {"Sybase_Unknown 0x00",0,0,0}, + {"bit",0,0,0}, + {"char",1,0,1}, + {"smallint",0,0,0}, + {"int",0,0,0}, + {"money",0,0,0}, + {"real",0,0,0}, + {"float",0,0,0}, + {"smalldatetime",0,0,0}, + {"Sybase_Unknown 0x09",0,0,0}, + {"varchar",1,0,1}, + {"varbinary",1,0,1}, + {"text",1,0,1}, + {"Sybase_Unknown 0x0d",0,0,0}, + {"Sybase_Unknown 0x0e",0,0,0}, + {"Sybase_Replication ID",0,0,0}, + {"numeric",1,1,0}, +}; + +/* Postgres data types */ +static MdbBackendType mdb_postgres_types[] = { + {"Postgres_Unknown 0x00",0,0,0}, + {"Bool",0,0,0}, + {"Int2",0,0,0}, + {"Int4",0,0,0}, + {"Int8",0,0,0}, + {"Money",0,0,0}, + {"Float4",0,0,0}, + {"Float8",0,0,0}, + {"Timestamp",0,0,0}, + {"Postgres_Unknown 0x09",0,0,0}, + {"Char",1,0,1}, + {"Postgres_Unknown 0x0b",0,0,0}, + {"Postgres_Unknown 0x0c",0,0,0}, + {"Postgres_Unknown 0x0d",0,0,0}, + {"Postgres_Unknown 0x0e",0,0,0}, + {"Serial",0,0,0}, + {"Postgres_Unknown 0x10",0,0,0}, +}; +/* MySQL data types */ +static MdbBackendType mdb_mysql_types[] = { + {"Text",1,0,1}, + {"char",0,0,0}, + {"int",0,0,0}, + {"int",0,0,0}, + {"int",0,0,0}, + {"float",0,0,0}, + {"float",0,0,0}, + {"float",0,0,0}, + {"date",0,0,1}, + {"varchar",1,0,1}, + {"varchar",1,0,1}, + {"varchar",1,0,1}, + {"text",1,0,1}, + {"blob",0,0,0}, + {"text",1,0,1}, + {"numeric",1,1,0}, + {"numeric",1,1,0}, +}; + +static gboolean mdb_drop_backend(gpointer key, gpointer value, gpointer data); + +char *mdb_get_coltype_string(MdbBackend *backend, int col_type) +{ + static char buf[16]; + + if (col_type > 0x10 ) { + // return NULL; + snprintf(buf,sizeof(buf), "type %04x", col_type); + return buf; + } else { + return backend->types_table[col_type].name; + } +} + +int mdb_coltype_takes_length(MdbBackend *backend, int col_type) +{ + return backend->types_table[col_type].needs_length; +} + +/** + * mdb_init_backends + * + * Initializes the mdb_backends hash and loads the builtin backends. + * Use mdb_remove_backends() to destroy this hash when done. + */ +void mdb_init_backends(void) +{ + mdb_backends = g_hash_table_new(g_str_hash, g_str_equal); + + mdb_register_backend(mdb_access_types, "access"); + mdb_register_backend(mdb_sybase_types, "sybase"); + mdb_register_backend(mdb_oracle_types, "oracle"); + mdb_register_backend(mdb_postgres_types, "postgres"); + mdb_register_backend(mdb_mysql_types, "mysql"); +} +void mdb_register_backend(MdbBackendType *backend_type, char *backend_name) +{ + MdbBackend *backend = (MdbBackend *) g_malloc0(sizeof(MdbBackend)); + backend->types_table = backend_type; + g_hash_table_insert(mdb_backends, backend_name, backend); +} + +/** + * mdb_remove_backends + * + * Removes all entries from and destroys the mdb_backends hash. + */ +void mdb_remove_backends(void) +{ + g_hash_table_foreach_remove(mdb_backends, mdb_drop_backend, NULL); + g_hash_table_destroy(mdb_backends); +} +static gboolean mdb_drop_backend(gpointer key, gpointer value, gpointer data) +{ + MdbBackend *backend = (MdbBackend *)value; + g_free (backend); + return TRUE; +} + +/** + * mdb_set_default_backend + * @mdb: Handle to open MDB database file + * @backend_name: Name of the backend to set as default + * + * Sets the default backend of the handle @mdb to @backend_name. + * + * Returns: 1 if successful, 0 if unsuccessful. + */ +int mdb_set_default_backend(MdbHandle *mdb, char *backend_name) +{ + MdbBackend *backend; + + backend = (MdbBackend *) g_hash_table_lookup(mdb_backends, backend_name); + if (backend) { + mdb->default_backend = backend; + mdb->backend_name = (char *) g_strdup(backend_name); + is_init = 0; + return 1; + } else { + return 0; + } +} + +/** + * mdb_get_relationships + * @mdb: Handle to open MDB database file + * + * Generates relationships by reading the MSysRelationships table. + * 'szColumn' contains the column name of the child table. + * 'szObject' contains the table name of the child table. + * 'szReferencedColumn' contains the column name of the parent table. + * 'szReferencedObject' contains the table name of the parent table. + * + * Returns: a string stating that relationships are not supported for the + * selected backend, or a string containing SQL commands for setting up + * the relationship, tailored for the selected backend. The caller is + * responsible for freeing this string. + */ +char *mdb_get_relationships(MdbHandle *mdb) +{ + unsigned int i; + gchar *text = NULL; /* String to be returned */ + static char *bound[4]; /* Bound values */ + static MdbTableDef *table; /* Relationships table */ + int backend = 0; /* Backends: 1=oracle */ + + if (strncmp(mdb->backend_name,"oracle",6) == 0) { + backend = 1; + } else { + if (is_init == 0) { /* the first time through */ + is_init = 1; + return (char *) g_strconcat( + "-- relationships are not supported for ", + mdb->backend_name, NULL); + } else { /* the second time through */ + is_init = 0; + return NULL; + } + } + + if (is_init == 0) { + table = mdb_read_table_by_name(mdb, "MSysRelationships", MDB_TABLE); + if ((!table) || (table->num_rows == 0)) { + return NULL; + } + + mdb_read_columns(table); + for (i=0;i<4;i++) { + bound[i] = (char *) g_malloc0(MDB_BIND_SIZE); + } + mdb_bind_column_by_name(table, "szColumn", bound[0]); + mdb_bind_column_by_name(table, "szObject", bound[1]); + mdb_bind_column_by_name(table, "szReferencedColumn", bound[2]); + mdb_bind_column_by_name(table, "szReferencedObject", bound[3]); + mdb_rewind_table(table); + + is_init = 1; + } + else if (table->cur_row >= table->num_rows) { /* past the last row */ + for (i=0;i<4;i++) + g_free(bound[i]); + is_init = 0; + return NULL; + } + + if (!mdb_fetch_row(table)) { + for (i=0;i<4;i++) + g_free(bound[i]); + is_init = 0; + return NULL; + } + + switch (backend) { + case 1: /* oracle */ + text = g_strconcat("alter table ", bound[1], + " add constraint ", bound[3], "_", bound[1], + " foreign key (", bound[0], ")" + " references ", bound[3], "(", bound[2], ")", NULL); + break; + } + + return (char *)text; +} + diff --git a/map/poi_geodownload/libmdb/catalog.c b/map/poi_geodownload/libmdb/catalog.c new file mode 100644 index 00000000..dc08abdd --- /dev/null +++ b/map/poi_geodownload/libmdb/catalog.c @@ -0,0 +1,138 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +char * +mdb_get_objtype_string(int obj_type) +{ +static char *type_name[] = {"Form", + "Table", + "Macro", + "System Table", + "Report", + "Query", + "Linked Table", + "Module", + "Relationship", + "Unknown 0x09", + "Unknown 0x0a", + "Database" + }; + + if (obj_type > 11) { + return NULL; + } else { + return type_name[obj_type]; + } +} + +void mdb_free_catalog(MdbHandle *mdb) +{ + unsigned int i; + + if (!mdb->catalog) return; + for (i=0; i<mdb->catalog->len; i++) + g_free (g_ptr_array_index(mdb->catalog, i)); + g_ptr_array_free(mdb->catalog, TRUE); + mdb->catalog = NULL; +} + +GPtrArray *mdb_read_catalog (MdbHandle *mdb, int objtype) +{ + MdbCatalogEntry *entry, msysobj; + MdbTableDef *table; + char obj_id[256]; + char obj_name[256]; + char obj_type[256]; + char obj_flags[256]; + int type; + + if (mdb->catalog) mdb_free_catalog(mdb); + mdb->catalog = g_ptr_array_new(); + mdb->num_catalog = 0; + + /* dummy up a catalog entry so we may read the table def */ + memset(&msysobj, 0, sizeof(MdbCatalogEntry)); + msysobj.mdb = mdb; + msysobj.object_type = MDB_TABLE; + msysobj.table_pg = 2; + strcpy(msysobj.object_name, "MSysObjects"); + + /* mdb_table_dump(&msysobj); */ + + table = mdb_read_table(&msysobj); + if (!table) return NULL; + + mdb_read_columns(table); + + mdb_bind_column_by_name(table, "Id", obj_id); + mdb_bind_column_by_name(table, "Name", obj_name); + mdb_bind_column_by_name(table, "Type", obj_type); + mdb_bind_column_by_name(table, "Flags", obj_flags); + + mdb_rewind_table(table); + + while (mdb_fetch_row(table)) { + type = atoi(obj_type); + if (objtype==MDB_ANY || type == objtype) { + // fprintf(stdout, "obj_id: %10ld objtype: %-3d obj_name: %s\n", + // (atol(obj_id) & 0x00FFFFFF), type, obj_name); + entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry)); + entry->mdb = mdb; + strcpy(entry->object_name, obj_name); + entry->object_type = (type & 0x7F); + entry->table_pg = atol(obj_id) & 0x00FFFFFF; + entry->flags = atol(obj_flags); + mdb->num_catalog++; + g_ptr_array_add(mdb->catalog, entry); + } + } + //mdb_dump_catalog(mdb, MDB_TABLE); + + mdb_free_tabledef(table); + + return mdb->catalog; +} + +void +mdb_dump_catalog(MdbHandle *mdb, int obj_type) +{ + unsigned int i; + MdbCatalogEntry *entry; + + mdb_read_catalog(mdb, obj_type); + for (i=0;i<mdb->num_catalog;i++) { + entry = g_ptr_array_index(mdb->catalog,i); + if (obj_type==MDB_ANY || entry->object_type==obj_type) { + fprintf(stdout,"Type: %-10s Name: %-18s T pg: %04x KKD pg: %04x row: %2d\n", + mdb_get_objtype_string(entry->object_type), + entry->object_name, + (unsigned int) entry->table_pg, + (unsigned int) entry->kkd_pg, + entry->kkd_rowid); + } + } + return; +} + diff --git a/map/poi_geodownload/libmdb/data.c b/map/poi_geodownload/libmdb/data.c new file mode 100644 index 00000000..f8749842 --- /dev/null +++ b/map/poi_geodownload/libmdb/data.c @@ -0,0 +1,858 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" +#include "time.h" +#include "math.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define OFFSET_MASK 0x1fff + +char *mdb_money_to_string(MdbHandle *mdb, int start, char *s); +static int _mdb_attempt_bind(MdbHandle *mdb, + MdbColumn *col, unsigned char isnull, int offset, int len); +static char *mdb_num_to_string(MdbHandle *mdb, int start, int datatype, int prec, int scale); +int mdb_copy_ole(MdbHandle *mdb, char *dest, int start, int size); + +static char date_fmt[64] = "%x %X"; + +void mdb_set_date_fmt(const char *fmt) +{ + date_fmt[63] = 0; + strncpy(date_fmt, fmt, 63); +} + +void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr) +{ +MdbColumn *col; + + /* + ** the column arrary is 0 based, so decrement to get 1 based parameter + */ + col=g_ptr_array_index(table->columns, col_num - 1); + col->bind_ptr = bind_ptr; +} +int +mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr) +{ + unsigned int i; + int col_num = -1; + MdbColumn *col; + + for (i=0;i<table->num_cols;i++) { + col=g_ptr_array_index(table->columns,i); + if (!strcmp(col->name,col_name)) { + col_num = col->col_num + 1; + mdb_bind_column(table, col_num, bind_ptr); + break; + } + } + + return col_num; +} +void mdb_bind_len(MdbTableDef *table, int col_num, int *len_ptr) +{ +MdbColumn *col; + + col=g_ptr_array_index(table->columns, col_num - 1); + col->len_ptr = len_ptr; +} + +/** + * mdb_find_pg_row + * @mdb: Database file handle + * @pg_row: Lower byte contains the row number, the upper three contain page + * @buf: Pointer for returning a pointer to the page + * @off: Pointer for returning an offset to the row + * @len: Pointer for returning the length of the row + * + * Returns: 0 on success. 1 on failure. + */ +int mdb_find_pg_row(MdbHandle *mdb, int pg_row, unsigned char **buf, int *off, size_t *len) +{ + unsigned int pg = pg_row >> 8; + unsigned int row = pg_row & 0xff; + + if (mdb_read_alt_pg(mdb, pg) != mdb->fmt->pg_size) + return 1; + mdb_swap_pgbuf(mdb); + *off = mdb_pg_get_int16(mdb, mdb->fmt->row_count_offset + 2 + (row*2)); + *len = mdb_find_end_of_row(mdb, row) - *off + 1; + mdb_swap_pgbuf(mdb); + *buf = mdb->alt_pg_buf; + return 0; +} + +int +mdb_find_end_of_row(MdbHandle *mdb, int row) +{ + MdbFormatConstants *fmt = mdb->fmt; + int row_end; + + /* Search the previous "row start" values for the first non-'lookupflag' one. + * If we don't find one, then the end of the page is the correct value. + */ +#if 1 + if (row==0) { + row_end = fmt->pg_size - 1; + } else { + row_end = (mdb_pg_get_int16(mdb, ((fmt->row_count_offset + 2) + (row - 1) * 2)) & OFFSET_MASK) - 1; + } + return row_end; +#else + int i, row_start; + + /* if lookupflag is not set, it's good (deleteflag is ok) */ + for (i = row - 1; i >= 0; i--) { + row_start = mdb_pg_get_int16(mdb, ((fmt->row_count_offset + 2) + i * 2)); + if (!(row_start & 0x8000)) { + break; + } + } + + if (i == -1) { + row_end = fmt->pg_size - 1; + } else { + row_end = (row_start & OFFSET_MASK) - 1; + } + return row_end; +#endif +} +#if 0 +static int mdb_is_null(unsigned char *null_mask, int col_num) +{ +int byte_num = (col_num - 1) / 8; +int bit_num = (col_num - 1) % 8; + + if ((1 << bit_num) & null_mask[byte_num]) { + return 0; + } else { + return 1; + } +} +#endif +/* bool has to be handled specially because it uses the null bit to store its +** value*/ +static int +mdb_xfer_bound_bool(MdbHandle *mdb, MdbColumn *col, int value) +{ + + col->cur_value_len = value; + if (col->bind_ptr) { + strcpy(col->bind_ptr, value ? "0" : "1"); + } + + return 0; +} +static int mdb_xfer_bound_ole(MdbHandle *mdb, int start, MdbColumn *col, int len) +{ + int ret = 0; + if (len) { + col->cur_value_start = start; + col->cur_value_len = len; + } else { + col->cur_value_start = 0; + col->cur_value_len = 0; + } + if (col->bind_ptr || col->len_ptr) { + //ret = mdb_copy_ole(mdb, col->bind_ptr, start, len); + memcpy(col->bind_ptr, &mdb->pg_buf[start], MDB_MEMO_OVERHEAD); + } + if (col->len_ptr) { + *col->len_ptr = MDB_MEMO_OVERHEAD; + } + return ret; +} +static int mdb_xfer_bound_data(MdbHandle *mdb, int start, MdbColumn *col, int len) +{ +int ret; + //if (!strcmp("Name",col->name)) { + //printf("start %d %d\n",start, len); + //} + if (len) { + col->cur_value_start = start; + col->cur_value_len = len; + } else { + col->cur_value_start = 0; + col->cur_value_len = 0; + } + if (col->bind_ptr) { + if (!len) { + strcpy(col->bind_ptr, ""); + } else if (col->col_type == MDB_NUMERIC) { + //fprintf(stdout,"len %d size %d\n",len, col->col_size); + char *str = mdb_num_to_string(mdb, start, col->col_type, + col->col_prec, col->col_scale); + strcpy(col->bind_ptr, str); + g_free(str); + } else { + //fprintf(stdout,"len %d size %d\n",len, col->col_size); + char *str = mdb_col_to_string(mdb, mdb->pg_buf, start, + col->col_type, len); + strcpy(col->bind_ptr, str); + + } + ret = strlen(col->bind_ptr); + if (col->len_ptr) { + *col->len_ptr = ret; + } + return ret; + } + return 0; +} +int mdb_read_row(MdbTableDef *table, unsigned int row) +{ + MdbHandle *mdb = table->entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + MdbColumn *col; + unsigned int i; + int rc; + int row_start, row_end; + int delflag, lookupflag; + MdbField fields[256]; + int num_fields; + + if (table->num_rows <= row) + return 0; + + row_start = mdb_pg_get_int16(mdb, (fmt->row_count_offset + 2) + (row*2)); + row_end = mdb_find_end_of_row(mdb, row); + + delflag = lookupflag = 0; + if (row_start & 0x8000) lookupflag++; + if (row_start & 0x4000) delflag++; + row_start &= OFFSET_MASK; /* remove flags */ +#if MDB_DEBUG + fprintf(stdout,"Row %d bytes %d to %d %s %s\n", + row, row_start, row_end, + lookupflag ? "[lookup]" : "", + delflag ? "[delflag]" : ""); +#endif + + if (!table->noskip_del && delflag) { + row_end = row_start-1; + return 0; + } + + num_fields = mdb_crack_row(table, row_start, row_end, fields); + if (!mdb_test_sargs(table, fields, num_fields)) return 0; + +#if MDB_DEBUG + fprintf(stdout,"sarg test passed row %d \n", row); +#endif + +#if MDB_DEBUG + buffer_dump(mdb->pg_buf, row_start, row_end); +#endif + + /* take advantage of mdb_crack_row() to clean up binding */ + /* use num_cols instead of num_fields -- bsb 03/04/02 */ + for (i = 0; i < table->num_cols; i++) { + col = g_ptr_array_index(table->columns,fields[i].colnum); + rc = _mdb_attempt_bind(mdb, col, fields[i].is_null, + fields[i].start, fields[i].siz); + } + + return 1; +} +static int _mdb_attempt_bind(MdbHandle *mdb, + MdbColumn *col, + unsigned char isnull, + int offset, + int len) +{ + if (col->col_type == MDB_BOOL) { + mdb_xfer_bound_bool(mdb, col, isnull); + } else if (isnull) { + mdb_xfer_bound_data(mdb, 0, col, 0); + } else if (col->col_type == MDB_OLE) { + mdb_xfer_bound_ole(mdb, offset, col, len); + } else { + //if (!mdb_test_sargs(mdb, col, offset, len)) { + //return 0; + //} + mdb_xfer_bound_data(mdb, offset, col, len); + } + return 1; +} +static int mdb_read_next_dpg(MdbTableDef *table) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + int next_pg; + +#ifndef SLOW_READ + next_pg = mdb_map_find_next(mdb, table->usage_map, + table->map_sz, table->cur_phys_pg); + + if (next_pg >= 0) { + if (mdb_read_pg(mdb, next_pg)) { + table->cur_phys_pg = next_pg; + return table->cur_phys_pg; + } else { + return 0; + } + } + fprintf(stderr, "Warning: defaulting to brute force read\n"); +#endif + /* can't do a fast read, go back to the old way */ + do { + if (!mdb_read_pg(mdb, table->cur_phys_pg++)) + return 0; + } while (mdb->pg_buf[0]!=0x01 || mdb_pg_get_int32(mdb, 4)!=entry->table_pg); + /* fprintf(stderr,"returning new page %ld\n", table->cur_phys_pg); */ + return table->cur_phys_pg; +} +int mdb_rewind_table(MdbTableDef *table) +{ + table->cur_pg_num=0; + table->cur_phys_pg=0; + table->cur_row=0; + + return 0; +} +int +mdb_fetch_row(MdbTableDef *table) +{ + MdbHandle *mdb = table->entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + unsigned int rows; + int rc; + guint32 pg; + + if (table->num_rows==0) + return 0; + + /* initialize */ + if (!table->cur_pg_num) { + table->cur_pg_num=1; + table->cur_row=0; + if ((!table->is_temp_table)&&(table->strategy!=MDB_INDEX_SCAN)) + if (!mdb_read_next_dpg(table)) return 0; + } + + do { + if (table->is_temp_table) { + GPtrArray *pages = table->temp_table_pages; + rows = mdb_get_int16( + g_ptr_array_index(pages, table->cur_pg_num-1), + fmt->row_count_offset); + if (table->cur_row >= rows) { + table->cur_row = 0; + table->cur_pg_num++; + if (table->cur_pg_num > pages->len) + return 0; + } + memcpy(mdb->pg_buf, + g_ptr_array_index(pages, table->cur_pg_num-1), + fmt->pg_size); + } else if (table->strategy==MDB_INDEX_SCAN) { + + if (!mdb_index_find_next(table->mdbidx, table->scan_idx, table->chain, &pg, (guint16 *) &(table->cur_row))) { + mdb_index_scan_free(table); + return 0; + } + mdb_read_pg(mdb, pg); + } else { + rows = mdb_pg_get_int16(mdb,fmt->row_count_offset); + + /* if at end of page, find a new page */ + if (table->cur_row >= rows) { + table->cur_row=0; + + if (!mdb_read_next_dpg(table)) { + return 0; + } + } + } + + /* printf("page %d row %d\n",table->cur_phys_pg, table->cur_row); */ + rc = mdb_read_row(table, table->cur_row); + table->cur_row++; + } while (!rc); + + return 1; +} +void mdb_data_dump(MdbTableDef *table) +{ + unsigned int i; + char *bound_values[MDB_MAX_COLS]; + + for (i=0;i<table->num_cols;i++) { + bound_values[i] = (char *) g_malloc(256); + mdb_bind_column(table, i+1, bound_values[i]); + } + mdb_rewind_table(table); + while (mdb_fetch_row(table)) { + for (i=0;i<table->num_cols;i++) { + fprintf(stdout, "column %d is %s\n", i+1, bound_values[i]); + } + } + for (i=0;i<table->num_cols;i++) { + g_free(bound_values[i]); + } +} + +int mdb_is_fixed_col(MdbColumn *col) +{ + return col->is_fixed; +} +#if 0 +static char *mdb_data_to_hex(MdbHandle *mdb, char *text, int start, int size) +{ +int i; + + for (i=start; i<start+size; i++) { + sprintf(&text[(i-start)*2],"%02x", mdb->pg_buf[i]); + } + text[(i-start)*2]='\0'; + + return text; +} +#endif +size_t +mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr) +{ + guint16 ole_len; + guint16 ole_flags; + unsigned char *buf; + int pg_row, row_start; + size_t len; + + ole_len = mdb_get_int16(ole_ptr, 0); + ole_flags = mdb_get_int16(ole_ptr, 2); + + if (ole_flags == 0x8000) { + /* inline fields don't have a next */ + return 0; + } else if (ole_flags == 0x4000) { + /* 0x4000 flagged ole's are contained on one page and thus + * should be handled entirely with mdb_ole_read() */ + return 0; + } else if (ole_flags == 0x0000) { + pg_row = (col->cur_blob_pg << 8) & col->cur_blob_row; + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + return 0; + } + if (col->bind_ptr) + memcpy(col->bind_ptr, buf + row_start, len); + pg_row = mdb_get_int32(buf, row_start); + col->cur_blob_pg = pg_row >> 8; + col->cur_blob_row = pg_row & 0xff; + + return len; + } + return 0; +} +size_t +mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, int chunk_size) +{ + guint16 ole_len; + guint16 ole_flags; + unsigned char *buf; + int pg_row, row_start; + size_t len; + + ole_len = mdb_get_int16(ole_ptr, 0); + ole_flags = mdb_get_int16(ole_ptr, 2); + mdb_debug(MDB_DEBUG_OLE,"ole len = %d ole flags = %08x", + ole_len, ole_flags); + + col->chunk_size = chunk_size; + + if (ole_flags == 0x8000) { + /* inline ole field, if we can satisfy it, then do it */ + len = col->cur_value_len - MDB_MEMO_OVERHEAD; + if (chunk_size >= len) { + if (col->bind_ptr) + memcpy(col->bind_ptr, + &mdb->pg_buf[col->cur_value_start + + MDB_MEMO_OVERHEAD], + len); + return len; + } else { + return 0; + } + } else if (ole_flags == 0x4000) { + pg_row = mdb_get_int32(ole_ptr, 4); + col->cur_blob_pg = pg_row >> 8; + col->cur_blob_row = pg_row & 0xff; + mdb_debug(MDB_DEBUG_OLE,"ole row = %d ole pg = %ld", + col->cur_blob_row, col->cur_blob_pg); + + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + return 0; + } + mdb_debug(MDB_DEBUG_OLE,"start %d len %d", row_start, len); + + if (col->bind_ptr) { + memcpy(col->bind_ptr, buf + row_start, len); + if (mdb_get_option(MDB_DEBUG_OLE)) + buffer_dump(col->bind_ptr, 0, 16); + } + return len; + } else if (ole_flags == 0x0000) { + pg_row = mdb_get_int32(ole_ptr, 4); + col->cur_blob_pg = pg_row >> 8; + col->cur_blob_row = pg_row & 0xff; + + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + return 0; + } + + if (col->bind_ptr) + memcpy(col->bind_ptr, buf + row_start, len); + + pg_row = mdb_get_int32(buf, row_start); + col->cur_blob_pg = pg_row >> 8; + col->cur_blob_row = pg_row & 0xff; + + return len; + } else { + fprintf(stderr,"Unhandled ole field flags = %04x\n", ole_flags); + return 0; + } +} +int mdb_copy_ole(MdbHandle *mdb, char *dest, int start, int size) +{ + guint16 ole_len; + guint16 ole_flags; + gint32 row_start, pg_row; + size_t len; + unsigned char *buf; + + if (size<MDB_MEMO_OVERHEAD) { + return 0; + } + + /* The 16 bit integer at offset 0 is the length of the memo field. + * The 32 bit integer at offset 4 contains page and row information. + */ + ole_len = mdb_pg_get_int16(mdb, start); + ole_flags = mdb_pg_get_int16(mdb, start+2); + + if (ole_flags == 0x8000) { + len = size - MDB_MEMO_OVERHEAD; + /* inline ole field */ + if (dest) memcpy(dest, &mdb->pg_buf[start + MDB_MEMO_OVERHEAD], + size - MDB_MEMO_OVERHEAD); + return len; + } else if (ole_flags == 0x4000) { + pg_row = mdb_get_int32(mdb->pg_buf, start+4); + mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x", pg_row >> 8); + + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + return 0; + } + mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d", + pg_row & 0xff, row_start, len); + + if (dest) + memcpy(dest, buf + row_start, len); + return len; + } else if (ole_flags == 0x0000) { + int cur = 0; + pg_row = mdb_get_int32(mdb->pg_buf, start+4); + mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x", pg_row >> 8); + do { + if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) { + return 0; + } + + mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d", + pg_row & 0xff, row_start, len); + + if (dest) + memcpy(dest+cur, buf + row_start + 4, len - 4); + cur += len - 4; + + /* find next lval page */ + pg_row = mdb_get_int32(buf, row_start); + } while ((pg_row >> 8)); + return cur; + } else { + fprintf(stderr,"Unhandled ole field flags = %04x\n", ole_flags); + return 0; + } +} +static char *mdb_memo_to_string(MdbHandle *mdb, int start, int size) +{ + guint16 memo_len; + static char text[MDB_BIND_SIZE]; + guint16 memo_flags; + gint32 row_start, pg_row; + size_t len; + unsigned char *buf; + + if (size<MDB_MEMO_OVERHEAD) { + return ""; + } + +#if MDB_DEBUG + buffer_dump(mdb->pg_buf, start, start + 12); +#endif + + /* The 16 bit integer at offset 0 is the length of the memo field. + * The 32 bit integer at offset 4 contains page and row information. + */ + memo_len = mdb_pg_get_int16(mdb, start); + memo_flags = mdb_pg_get_int16(mdb, start+2); + + if (memo_flags & 0x8000) { + /* inline memo field */ + strncpy(text, (char *)&mdb->pg_buf[start + MDB_MEMO_OVERHEAD], + size - MDB_MEMO_OVERHEAD); + text[size - MDB_MEMO_OVERHEAD]='\0'; + return text; + } else if (memo_flags & 0x4000) { + pg_row = mdb_get_int32(mdb->pg_buf, start+4); +#if MDB_DEBUG + printf("Reading LVAL page %06x\n", pg_row >> 8); +#endif + if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) { + return ""; + } +#if MDB_DEBUG + printf("row num %d start %d len %d\n", + pg_row & 0xff, row_start, len); + buffer_dump(mdb->pg_buf, row_start, row_start + len); +#endif + if (IS_JET3(mdb)) { + strncpy(text, (char*) buf + row_start, len); + text[len]='\0'; + } else { + mdb_unicode2ascii(mdb, buf, row_start, len, text); + } + return text; + } else { /* if (memo_flags == 0x0000) { */ + pg_row = mdb_get_int32(mdb->pg_buf, start+4); +#if MDB_DEBUG + printf("Reading LVAL page %06x\n", pg_row >> 8); +#endif + text[0]='\0'; + do { + if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) { + return ""; + } +#if MDB_DEBUG + printf("row num %d start %d len %d\n", + pg_row & 0xff, row_start, len); +#endif + strncat(text, (char*) buf + row_start + 4, + strlen(text) + len - 4 > MDB_BIND_SIZE ? + MDB_BIND_SIZE - strlen(text) : len - 4); + + /* find next lval page */ + pg_row = mdb_get_int32(mdb->pg_buf, row_start); + } while ((pg_row >> 8)); + return text; +/* + } else { + fprintf(stderr,"Unhandled memo field flags = %04x\n", memo_flags); + return ""; +*/ + } +} +static char * +mdb_num_to_string(MdbHandle *mdb, int start, int datatype, int prec, int scale) +{ + char *text; + gint32 l; + + memcpy(&l, mdb->pg_buf+start+13, 4); + + text = (char *) g_malloc(prec+2); + sprintf(text, "%0*" G_GINT32_FORMAT, prec, GINT32_FROM_LE(l)); + if (scale) { + memmove(text+prec-scale, text+prec-scale+1, scale+1); + text[prec-scale] = '.'; + } + return text; +} + +static int trim_trailing_zeros(char * buff, int n) +{ + char * p = buff + n - 1; + + while (p >= buff && *p == '0') + *p-- = '\0'; + + if (*p == '.') + *p = '\0'; + + return 0; +} + +char *mdb_col_to_string(MdbHandle *mdb, unsigned char *buf, int start, int datatype, int size) +{ + /* FIX ME -- not thread safe */ + static char text[MDB_BIND_SIZE]; + time_t t; + int n; + float tf; + double td; + + switch (datatype) { + case MDB_BOOL: + /* shouldn't happen. bools are handled specially + ** by mdb_xfer_bound_bool() */ + break; + case MDB_BYTE: + sprintf(text,"%d",mdb_get_byte(buf, start)); + return text; + break; + case MDB_INT: + sprintf(text,"%ld",(long)mdb_get_int16(buf, start)); + return text; + break; + case MDB_LONGINT: + sprintf(text,"%ld",mdb_get_int32(buf, start)); + return text; + break; + case MDB_FLOAT: + tf = mdb_get_single(mdb->pg_buf, start); + n = sprintf(text,"%.*f",FLT_DIG - (int)ceil(log10(tf)), tf); + trim_trailing_zeros(text, n); + return text; + break; + case MDB_DOUBLE: + td = mdb_get_double(mdb->pg_buf, start); + n = sprintf(text,"%.*f",DBL_DIG - (int)ceil(log10(td)), td); + trim_trailing_zeros(text, n); + return text; + break; + case MDB_TEXT: + if (size<0) { + return ""; + } + if (IS_JET4(mdb)) { +/* + int i; + for (i=0;i<size;i++) { + fprintf(stdout, "%c %02x ", mdb->pg_buf[start+i], mdb->pg_buf[start+i]); + } + fprintf(stdout, "\n"); +*/ + mdb_unicode2ascii(mdb, mdb->pg_buf, start, size, text); + } else { + strncpy(text,(char*) &buf[start], size); + text[size]='\0'; + } + return text; + break; + case MDB_SDATETIME: + td = mdb_get_double(mdb->pg_buf, start); + if (td > 1) { + t = (long int)((td - 25569.0) * 86400.0); + } else { + t = (long int)(td * 86400.0); + } + strftime(text, MDB_BIND_SIZE, date_fmt, (struct tm*)gmtime(&t)); + return text; + + break; + case MDB_MEMO: + return mdb_memo_to_string(mdb, start, size); + break; + case MDB_MONEY: + mdb_money_to_string(mdb, start, text); + return text; + case MDB_NUMERIC: + break; + default: + return ""; + break; + } + return NULL; +} +int mdb_col_disp_size(MdbColumn *col) +{ + switch (col->col_type) { + case MDB_BOOL: + return 1; + break; + case MDB_BYTE: + return 4; + break; + case MDB_INT: + return 6; + break; + case MDB_LONGINT: + return 11; + break; + case MDB_FLOAT: + return 10; + break; + case MDB_DOUBLE: + return 10; + break; + case MDB_TEXT: + return col->col_size; + break; + case MDB_SDATETIME: + return 20; + break; + case MDB_MEMO: + return 255; + break; + case MDB_MONEY: + return 21; + break; + } + return 0; +} +int mdb_col_fixed_size(MdbColumn *col) +{ + switch (col->col_type) { + case MDB_BOOL: + return 1; + break; + case MDB_BYTE: + return -1; + break; + case MDB_INT: + return 2; + break; + case MDB_LONGINT: + return 4; + break; + case MDB_FLOAT: + return 4; + break; + case MDB_DOUBLE: + return 8; + break; + case MDB_TEXT: + return -1; + break; + case MDB_SDATETIME: + return 4; + break; + case MDB_MEMO: + return -1; + break; + case MDB_MONEY: + return 8; + break; + } + return 0; +} diff --git a/map/poi_geodownload/libmdb/dump.c b/map/poi_geodownload/libmdb/dump.c new file mode 100644 index 00000000..630ce2ea --- /dev/null +++ b/map/poi_geodownload/libmdb/dump.c @@ -0,0 +1,41 @@ +#include <ctype.h> +#include <string.h> +#include <stdio.h> +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +void buffer_dump(const unsigned char* buf, int start, int end) +{ + char asc[20]; + int j, k; + + memset(asc, 0, sizeof(asc)); + k = 0; + for (j=start; j<=end; j++) { + if (k == 0) { + fprintf(stdout, "%04x ", j); + } + fprintf(stdout, "%02x ", buf[j]); + asc[k] = isprint(buf[j]) ? buf[j] : '.'; + k++; + if (k == 8) { + fprintf(stdout, " "); + } + if (k == 16) { + fprintf(stdout, " %s\n", asc); + memset(asc, 0, sizeof(asc)); + k = 0; + } + } + for (j=k; j<16; j++) { + fprintf(stdout, " "); + } + if (k < 8) { + fprintf(stdout, " "); + } + fprintf(stdout, " %s\n", asc); +} diff --git a/map/poi_geodownload/libmdb/file.c b/map/poi_geodownload/libmdb/file.c new file mode 100644 index 00000000..29174f22 --- /dev/null +++ b/map/poi_geodownload/libmdb/file.c @@ -0,0 +1,379 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* +typedef struct { + int pg_size; + guint16 row_count_offset; + guint16 tab_num_rows_offset; + guint16 tab_num_cols_offset; + guint16 tab_num_idxs_offset; + guint16 tab_num_ridxs_offset; + guint16 tab_usage_map_offset; + guint16 tab_first_dpg_offset; + guint16 tab_cols_start_offset; + guint16 tab_ridx_entry_size; + guint16 col_fixed_offset; + guint16 col_size_offset; + guint16 col_num_offset; + guint16 tab_col_entry_size; + guint16 tab_free_map_offset; + guint16 tab_col_offset_var; + guint16 tab_col_offset_fixed; + guint16 tab_row_col_num_offset; +} MdbFormatConstants; +*/ +MdbFormatConstants MdbJet4Constants = { + 4096, 0x0c, 16, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25, 59, 7, 21, 9 +}; +MdbFormatConstants MdbJet3Constants = { + 2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18, 39, 3, 14, 5 /* not sure on 5, need to check */ +}; + +static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg); + +/** + * mdb_find_file: + * @filename: path to MDB (database) file + * + * Finds and returns the absolute path to an MDB file. Function will first try + * to fstat file as passed, then search through the $MDBPATH if not found. + * + * Return value: gchar pointer to absolute path. Caller is responsible for + * freeing. + **/ + +static gchar *mdb_find_file(const char *file_name) +{ + struct stat status; + gchar *mdbpath, **dir, *tmpfname; + unsigned int i = 0; + + /* try the provided file name first */ + if (!stat(file_name, &status)) { + return g_strdup(file_name); + } + + /* Now pull apart $MDBPATH and try those */ + mdbpath = (gchar *) getenv("MDBPATH"); + /* no path, can't find file */ + if (!mdbpath || !strlen(mdbpath)) return NULL; + + dir = g_strsplit(mdbpath, ":", 0); + while (dir[i]) { + if (!strlen(dir[i])) continue; + tmpfname = g_strconcat(dir[i++], "/", file_name, NULL); + if (!stat(tmpfname, &status)) { + g_strfreev(dir); + return tmpfname; + } + g_free(tmpfname); + } + g_strfreev(dir); + return NULL; +} +/** + * mdb_open: + * @filename: path to MDB (database) file + * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write + * + * Opens an MDB file and returns an MdbHandle to it. MDB File may be relative + * to the current directory, a full path to the file, or relative to a + * component of $MDBPATH. + * + * Return value: pointer to MdbHandle structure. + **/ +MdbHandle *mdb_open(const char *filename, MdbFileFlags flags) +{ + MdbHandle *mdb; + + mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle)); + mdb_set_default_backend(mdb, "access"); + /* need something to bootstrap with, reassign after page 0 is read */ + mdb->fmt = &MdbJet3Constants; + mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile)); + mdb->f->refs = 1; + mdb->f->fd = -1; + mdb->f->filename = (char *) mdb_find_file(filename); + if (!mdb->f->filename) { + fprintf(stderr, "Can't alloc filename\n"); + mdb_close(mdb); + return NULL; + } + if (flags & MDB_WRITABLE) { + mdb->f->writable = TRUE; + mdb->f->fd = open(mdb->f->filename,O_RDWR); + } else { + mdb->f->fd = open(mdb->f->filename,O_RDONLY); + } + + if (mdb->f->fd==-1) { + fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename); + mdb_close(mdb); + return NULL; + } + if (!mdb_read_pg(mdb, 0)) { + fprintf(stderr,"Couldn't read first page.\n"); + mdb_close(mdb); + return NULL; + } + if (mdb->pg_buf[0] != 0) { + mdb_close(mdb); + return NULL; + } + mdb->f->jet_version = mdb_pg_get_int32(mdb, 0x14); + if (IS_JET4(mdb)) { + mdb->fmt = &MdbJet4Constants; + } else if (IS_JET3(mdb)) { + mdb->fmt = &MdbJet3Constants; + } else { + fprintf(stderr,"Unknown Jet version.\n"); + mdb_close(mdb); + return NULL; + } + + return mdb; +} + +/** + * mdb_close: + * @mdb: Handle to open MDB database file + * + * Dereferences MDB file, closes if reference count is 0, and destroys handle. + * + **/ +void +mdb_close(MdbHandle *mdb) +{ + if (!mdb) return; + mdb_free_catalog(mdb); + g_free(mdb->stats); + g_free(mdb->backend_name); + + if (mdb->f) { + if (mdb->f->refs > 1) { + mdb->f->refs--; + } else { + if (mdb->f->fd != -1) close(mdb->f->fd); + g_free(mdb->f->filename); + g_free(mdb->f); + } + } + + g_free(mdb); +} +/** + * mdb_clone_handle: + * @mdb: Handle to open MDB database file + * + * Clones an existing database handle. Cloned handle shares the file descriptor + * but has its own page buffer, page position, and similar internal variables. + * + * Return value: new handle to the database. + */ +MdbHandle *mdb_clone_handle(MdbHandle *mdb) +{ + MdbHandle *newmdb; + MdbCatalogEntry *entry, *data; + unsigned int i; + + newmdb = (MdbHandle *) g_memdup(mdb, sizeof(MdbHandle)); + newmdb->stats = NULL; + newmdb->catalog = g_ptr_array_new(); + for (i=0;i<mdb->num_catalog;i++) { + entry = g_ptr_array_index(mdb->catalog,i); + data = g_memdup(entry,sizeof(MdbCatalogEntry)); + g_ptr_array_add(newmdb->catalog, data); + } + mdb->backend_name = NULL; + if (mdb->f) { + mdb->f->refs++; + } + return newmdb; +} + +/* +** mdb_read a wrapper for read that bails if anything is wrong +*/ +ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg) +{ + ssize_t len; + + if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size; + + len = _mdb_read_pg(mdb, mdb->pg_buf, pg); + //fprintf(stderr, "read page %d type %02x\n", pg, mdb->pg_buf[0]); + mdb->cur_pg = pg; + /* kan - reset the cur_pos on a new page read */ + mdb->cur_pos = 0; /* kan */ + return len; +} +ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg) +{ + ssize_t len; + + len = _mdb_read_pg(mdb, mdb->alt_pg_buf, pg); + return len; +} +static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg) +{ + ssize_t len; + struct stat status; + off_t offset = pg * mdb->fmt->pg_size; + + fstat(mdb->f->fd, &status); + if (status.st_size < offset) { + fprintf(stderr,"offset %lu is beyond EOF\n",offset); + return 0; + } + if (mdb->stats && mdb->stats->collect) + mdb->stats->pg_reads++; + + lseek(mdb->f->fd, offset, SEEK_SET); + len = read(mdb->f->fd,pg_buf,mdb->fmt->pg_size); + if (len==-1) { + perror("read"); + return 0; + } + else if (len<mdb->fmt->pg_size) { + /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */ + return 0; + } + return len; +} +void mdb_swap_pgbuf(MdbHandle *mdb) +{ +char tmpbuf[MDB_PGSIZE]; + + memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE); + memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE); + memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE); +} + + +/* really stupid, just here for consistancy */ +unsigned char mdb_get_byte(unsigned char *buf, int offset) +{ + return buf[offset]; +} +unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset) +{ + if (offset < 0 || offset+1 > mdb->fmt->pg_size) return -1; + mdb->cur_pos++; + return mdb->pg_buf[offset]; +} + +int mdb_get_int16(unsigned char *buf, int offset) +{ + return buf[offset+1]*256+buf[offset]; +} +int mdb_pg_get_int16(MdbHandle *mdb, int offset) +{ + if (offset < 0 || offset+2 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=2; + return mdb_get_int16(mdb->pg_buf, offset); +} + +gint32 mdb_pg_get_int24_msb(MdbHandle *mdb, int offset) +{ + gint32 l = 0; + if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=3; + memcpy((char *)&l+1, &(mdb->pg_buf[offset]), 3); +#if 0 + printf("l=0x%08x 0x%08x\n",l,GINT32_FROM_BE(l)); +#endif + return GINT32_FROM_BE(l); +} +gint32 mdb_get_int24(unsigned char *buf, int offset) +{ + gint32 l = 0; + memcpy(&l, &buf[offset], 3); + return GINT32_FROM_LE(l); +} +gint32 mdb_pg_get_int24(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=3; + return mdb_get_int24(mdb->pg_buf, offset); +} + +long mdb_get_int32(unsigned char *buf, int offset) +{ + guint32 l; + memcpy(&l, &buf[offset], 4); + return (long)GINT32_FROM_LE(l); +} +long mdb_pg_get_int32(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=4; + return mdb_get_int32(mdb->pg_buf, offset); +} + +float mdb_get_single(unsigned char *buf, int offset) +{ + union {guint32 g; float f;} f; + memcpy(&f, &buf[offset], 4); + f.g = GUINT32_FROM_LE(f.g); + return f.f; +} +float mdb_pg_get_single(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=4; + return mdb_get_single(mdb->pg_buf, offset); +} + +double mdb_get_double(unsigned char *buf, int offset) +{ + union {guint64 g; double d;} d; + memcpy(&d, &buf[offset], 8); + d.g = GUINT64_FROM_LE(d.g); + return d.d; +} +double mdb_pg_get_double(MdbHandle *mdb, int offset) +{ + if (offset <0 || offset+8 > mdb->fmt->pg_size) return -1; + mdb->cur_pos+=8; + return mdb_get_double(mdb->pg_buf, offset); +} + +#if 0 +int +mdb_set_pos(MdbHandle *mdb, int pos) +{ + if (pos<0 || pos >= mdb->fmt->pg_size) return 0; + + mdb->cur_pos=pos; + return pos; +} + +int mdb_get_pos(MdbHandle *mdb) +{ + return mdb->cur_pos; +} +#endif + diff --git a/map/poi_geodownload/libmdb/iconv.c b/map/poi_geodownload/libmdb/iconv.c new file mode 100644 index 00000000..4afb604a --- /dev/null +++ b/map/poi_geodownload/libmdb/iconv.c @@ -0,0 +1,63 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +int +mdb_unicode2ascii(MdbHandle *mdb, unsigned char *buf, int offset, unsigned int len, char *dest) +{ + unsigned int i; + + if (buf[offset]==0xff && buf[offset+1]==0xfe) { + strncpy(dest, (char*) &buf[offset+2], len-2); + dest[len-2]='\0'; + } else { + /* convert unicode to ascii, rather sloppily */ + for (i=0;i<len;i+=2) + dest[i/2] = buf[offset + i]; + dest[len/2]='\0'; + } + return len; +} + +int +mdb_ascii2unicode(MdbHandle *mdb, unsigned char *buf, int offset, unsigned int len, char *dest) +{ + unsigned int i = 0; + + if (!buf) return 0; + + if (IS_JET3(mdb)) { + strncpy(dest, (char*) &buf[offset], len); + dest[len]='\0'; + return strlen(dest); + } + + while (i<strlen((char*)&buf[offset]) && (i*2+2)<len) { + dest[i*2] = buf[offset+i]; + dest[i*2+1] = 0; + i++; + } + + return (i*2); +} diff --git a/map/poi_geodownload/libmdb/include/Makefile.am b/map/poi_geodownload/libmdb/include/Makefile.am new file mode 100644 index 00000000..084a7542 --- /dev/null +++ b/map/poi_geodownload/libmdb/include/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = mdbtools.h diff --git a/map/poi_geodownload/libmdb/include/mdbtools.h b/map/poi_geodownload/libmdb/include/mdbtools.h new file mode 100644 index 00000000..0f8e9aa6 --- /dev/null +++ b/map/poi_geodownload/libmdb/include/mdbtools.h @@ -0,0 +1,544 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef _mdbtools_h_ +#define _mdbtools_h_ + +#ifdef __cplusplus + extern "C" { +#endif +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <glib.h> + +#ifdef HAVE_ICONV +#include <iconv.h> +#endif + +#define MDB_DEBUG 0 + +#define MDB_PGSIZE 4096 +#define MDB_MAX_OBJ_NAME 256 +#define MDB_MAX_COLS 256 +#define MDB_MAX_IDX_COLS 10 +#define MDB_CATALOG_PG 18 +#define MDB_MEMO_OVERHEAD 12 +#define MDB_BIND_SIZE 16384 + +enum { + MDB_PAGE_DB = 0, + MDB_PAGE_DATA, + MDB_PAGE_TABLE, + MDB_PAGE_INDEX, + MDB_PAGE_LEAF, + MDB_PAGE_MAP +}; +enum { + MDB_VER_JET3 = 0, + MDB_VER_JET4 = 1 +}; +enum { + MDB_FORM = 0, + MDB_TABLE, + MDB_MACRO, + MDB_SYSTEM_TABLE, + MDB_REPORT, + MDB_QUERY, + MDB_LINKED_TABLE, + MDB_MODULE, + MDB_RELATIONSHIP, + MDB_UNKNOWN_09, + MDB_UNKNOWN_0A, + MDB_DATABASE_PROPERTY, + MDB_ANY = -1 +}; +enum { + MDB_BOOL = 0x01, + MDB_BYTE = 0x02, + MDB_INT = 0x03, + MDB_LONGINT = 0x04, + MDB_MONEY = 0x05, + MDB_FLOAT = 0x06, + MDB_DOUBLE = 0x07, + MDB_SDATETIME = 0x08, + MDB_TEXT = 0x0a, + MDB_OLE = 0x0b, + MDB_MEMO = 0x0c, + MDB_REPID = 0x0f, + MDB_NUMERIC = 0x10 +}; + +/* SARG operators */ +enum { + MDB_OR = 1, + MDB_AND, + MDB_NOT, + MDB_EQUAL, + MDB_GT, + MDB_LT, + MDB_GTEQ, + MDB_LTEQ, + MDB_LIKE, + MDB_ISNULL, + MDB_NOTNULL +}; + +typedef enum { + MDB_TABLE_SCAN, + MDB_LEAF_SCAN, + MDB_INDEX_SCAN +} MdbStrategy; + +typedef enum { + MDB_NOFLAGS = 0x00, + MDB_WRITABLE = 0x01 +} MdbFileFlags; + +enum { + MDB_DEBUG_LIKE = 0x0001, + MDB_DEBUG_WRITE = 0x0002, + MDB_DEBUG_USAGE = 0x0004, + MDB_DEBUG_OLE = 0x0008, + MDB_DEBUG_ROW = 0x0010, + MDB_USE_INDEX = 0x0020, + MDB_NO_MEMO = 0x0040 /* don't follow memo fields */ +}; + +#define mdb_is_logical_op(x) (x == MDB_OR || \ + x == MDB_AND || \ + x == MDB_NOT ) + +#define mdb_is_relational_op(x) (x == MDB_EQUAL || \ + x == MDB_GT || \ + x == MDB_LT || \ + x == MDB_GTEQ || \ + x == MDB_LTEQ || \ + x == MDB_LIKE || \ + x == MDB_ISNULL || \ + x == MDB_NOTNULL ) + +enum { + MDB_ASC, + MDB_DESC +}; + +enum { + MDB_IDX_UNIQUE = 0x01, + MDB_IDX_IGNORENULLS = 0x02, + MDB_IDX_REQUIRED = 0x08 +}; + +#define IS_JET4(mdb) (mdb->f->jet_version==MDB_VER_JET4) +#define IS_JET3(mdb) (mdb->f->jet_version==MDB_VER_JET3) + +/* hash to store registered backends */ +/* extern GHashTable *mdb_backends; */ + +/* forward declarations */ +typedef struct mdbindex MdbIndex; +typedef struct mdbsargtree MdbSargNode; + +typedef struct { + char *name; + unsigned char needs_length; /* or precision */ + unsigned char needs_scale; + unsigned char needs_quotes; +} MdbBackendType; + +typedef struct { + MdbBackendType *types_table; +} MdbBackend; + +typedef struct { + gboolean collect; + unsigned long pg_reads; +} MdbStatistics; + +typedef struct { + int fd; + gboolean writable; + char *filename; + guint32 jet_version; + guint32 db_key; + char db_passwd[14]; + MdbBackend *default_backend; + char *backend_name; + MdbStatistics *stats; + /* free map */ + int map_sz; + unsigned char *free_map; + /* reference count */ + int refs; +} MdbFile; + +/* offset to row count on data pages...version dependant */ +typedef struct { + ssize_t pg_size; + guint16 row_count_offset; + guint16 tab_num_rows_offset; + guint16 tab_num_cols_offset; + guint16 tab_num_idxs_offset; + guint16 tab_num_ridxs_offset; + guint16 tab_usage_map_offset; + guint16 tab_first_dpg_offset; + guint16 tab_cols_start_offset; + guint16 tab_ridx_entry_size; + guint16 col_fixed_offset; + guint16 col_size_offset; + guint16 col_num_offset; + guint16 tab_col_entry_size; + guint16 tab_free_map_offset; + guint16 tab_col_offset_var; + guint16 tab_col_offset_fixed; + guint16 tab_row_col_num_offset; +} MdbFormatConstants; + +typedef struct { + MdbFile *f; + guint32 cur_pg; + guint16 row_num; + unsigned int cur_pos; + unsigned char pg_buf[MDB_PGSIZE]; + unsigned char alt_pg_buf[MDB_PGSIZE]; + unsigned int num_catalog; + GPtrArray *catalog; + MdbBackend *default_backend; + char *backend_name; + MdbFormatConstants *fmt; + MdbStatistics *stats; +#ifdef HAVE_ICONV + iconv_t iconv_in; + iconv_t iconv_out; +#endif +} MdbHandle; + +typedef struct { + MdbHandle *mdb; + char object_name[MDB_MAX_OBJ_NAME+1]; + int object_type; + unsigned long table_pg; /* misnomer since object may not be a table */ + unsigned long kkd_pg; + unsigned int kkd_rowid; + int num_props; + GArray *props; + GArray *columns; + int flags; +} MdbCatalogEntry; + +typedef struct { + gchar *name; + GHashTable *hash; +} MdbProperties; + +typedef union { + int i; + double d; + char s[256]; +} MdbAny; + +typedef struct { + char name[MDB_MAX_OBJ_NAME+1]; + int col_type; + int col_size; + void *bind_ptr; + int *len_ptr; + GHashTable *properties; + unsigned int num_sargs; + GPtrArray *sargs; + GPtrArray *idx_sarg_cache; + unsigned char is_fixed; + int query_order; + /* col_num is the current column order, + * does not include deletes */ + int col_num; + int cur_value_start; + int cur_value_len; + /* MEMO/OLE readers */ + guint32 cur_blob_pg; + guint32 cur_blob_row; + int chunk_size; + /* numerics only */ + int col_prec; + int col_scale; + MdbProperties *props; + /* info needed for handling deleted/added columns */ + int fixed_offset; + unsigned int var_col_num; + /* row_col_num is the row column number order, + * including deleted columns */ + int row_col_num; +} MdbColumn; + +struct mdbsargtree { + int op; + MdbColumn *col; + MdbAny value; + void *parent; + MdbSargNode *left; + MdbSargNode *right; +}; + +typedef struct { + guint32 pg; + int start_pos; + int offset; + int len; + guint16 idx_starts[2000]; + unsigned char cache_value[256]; +} MdbIndexPage; + +typedef int (*MdbSargTreeFunc)(MdbSargNode *, gpointer data); + +#define MDB_MAX_INDEX_DEPTH 10 + +typedef struct { + int cur_depth; + guint32 last_leaf_found; + int clean_up_mode; + MdbIndexPage pages[MDB_MAX_INDEX_DEPTH]; +} MdbIndexChain; + +typedef struct { + MdbCatalogEntry *entry; + char name[MDB_MAX_OBJ_NAME+1]; + unsigned int num_cols; + GPtrArray *columns; + unsigned int num_rows; + int index_start; + unsigned int num_real_idxs; + unsigned int num_idxs; + GPtrArray *indices; + guint32 first_data_pg; + guint32 cur_pg_num; + guint32 cur_phys_pg; + unsigned int cur_row; + int noskip_del; /* don't skip deleted rows */ + /* object allocation map */ + guint32 map_base_pg; + unsigned int map_sz; + unsigned char *usage_map; + /* pages with free space left */ + guint32 freemap_base_pg; + unsigned int freemap_sz; + unsigned char *free_usage_map; + /* query planner */ + MdbSargNode *sarg_tree; + MdbStrategy strategy; + MdbIndex *scan_idx; + MdbHandle *mdbidx; + MdbIndexChain *chain; + MdbProperties *props; + unsigned int num_var_cols; /* to know if row has variable columns */ + /* temp table */ + unsigned int is_temp_table; + GPtrArray *temp_table_pages; +} MdbTableDef; + +struct mdbindex { + int index_num; + char name[MDB_MAX_OBJ_NAME+1]; + unsigned char index_type; + guint32 first_pg; + int num_rows; /* number rows in index */ + unsigned int num_keys; + short key_col_num[MDB_MAX_IDX_COLS]; + unsigned char key_col_order[MDB_MAX_IDX_COLS]; + unsigned char flags; + MdbTableDef *table; +}; + +typedef struct { + char name[MDB_MAX_OBJ_NAME+1]; +} MdbColumnProp; + +typedef struct { + void *value; + int siz; + int start; + unsigned char is_null; + unsigned char is_fixed; + int colnum; + int offset; +} MdbField; + +typedef struct { + int op; + MdbAny value; +} MdbSarg; + +/* mem.c */ +extern void mdb_init(void); +extern void mdb_exit(void); + +/* file.c */ +extern ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg); +extern ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg); +extern unsigned char mdb_get_byte(unsigned char *buf, int offset); +extern int mdb_get_int16(unsigned char *buf, int offset); +extern gint32 mdb_get_int24(unsigned char *buf, int offset); +extern long mdb_get_int32(unsigned char *buf, int offset); +extern float mdb_get_single(unsigned char *buf, int offset); +extern double mdb_get_double(unsigned char *buf, int offset); +extern unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset); +extern int mdb_pg_get_int16(MdbHandle *mdb, int offset); +extern gint32 mdb_pg_get_int24(MdbHandle *mdb, int offset); +extern long mdb_pg_get_int32(MdbHandle *mdb, int offset); +extern float mdb_pg_get_single(MdbHandle *mdb, int offset); +extern double mdb_pg_get_double(MdbHandle *mdb, int offset); +extern gint32 mdb_pg_get_int24_msb(MdbHandle *mdb, int offset); +extern MdbHandle *mdb_open(const char *filename, MdbFileFlags flags); +extern void mdb_close(MdbHandle *mdb); +extern MdbHandle *mdb_clone_handle(MdbHandle *mdb); +extern void mdb_swap_pgbuf(MdbHandle *mdb); +extern long _mdb_get_int32(unsigned char *buf, int offset); + +/* catalog.c */ +extern void mdb_free_catalog(MdbHandle *mdb); +extern GPtrArray *mdb_read_catalog(MdbHandle *mdb, int obj_type); +extern void mdb_dump_catalog(MdbHandle *mdb, int obj_type); +extern char *mdb_get_objtype_string(int obj_type); + +/* table.c */ +extern MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry); +extern void mdb_free_tabledef(MdbTableDef *table); +extern MdbTableDef *mdb_read_table(MdbCatalogEntry *entry); +extern MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type); +extern void mdb_append_column(GPtrArray *columns, MdbColumn *in_col); +extern void mdb_free_columns(GPtrArray *columns); +extern GPtrArray *mdb_read_columns(MdbTableDef *table); +extern void mdb_table_dump(MdbCatalogEntry *entry); +extern guint16 read_pg_if_16(MdbHandle *mdb, int *cur_pos); +extern guint32 read_pg_if_32(MdbHandle *mdb, int *cur_pos); +extern int read_pg_if(MdbHandle *mdb, int *cur_pos, int offset); +extern guint16 read_pg_if_n(MdbHandle *mdb, unsigned char *buf, int *cur_pos, int len); +extern int mdb_is_user_table(MdbCatalogEntry *entry); +extern int mdb_is_system_table(MdbCatalogEntry *entry); + +/* data.c */ +extern int mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr); +extern void mdb_data_dump(MdbTableDef *table); +extern void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr); +extern int mdb_rewind_table(MdbTableDef *table); +extern int mdb_fetch_row(MdbTableDef *table); +extern int mdb_is_fixed_col(MdbColumn *col); +extern char *mdb_col_to_string(MdbHandle *mdb, unsigned char *buf, int start, int datatype, int size); +extern int mdb_find_pg_row(MdbHandle *mdb, int pg_row, unsigned char **buf, int *off, size_t *len); +extern int mdb_find_end_of_row(MdbHandle *mdb, int row); +extern int mdb_col_fixed_size(MdbColumn *col); +extern int mdb_col_disp_size(MdbColumn *col); +extern void mdb_bind_len(MdbTableDef *table, int col_num, int *len_ptr); +extern size_t mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr); +extern size_t mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, int chunk_size); +extern void mdb_set_date_fmt(const char *); +extern int mdb_read_row(MdbTableDef *table, unsigned int row); + +/* dump.c */ +extern void buffer_dump(const unsigned char* buf, int start, int end); + +/* backend.c */ +extern char *mdb_get_coltype_string(MdbBackend *backend, int col_type); +extern int mdb_coltype_takes_length(MdbBackend *backend, int col_type); +extern void mdb_init_backends(void); +extern void mdb_register_backend(MdbBackendType *backend, char *backend_name); +extern void mdb_remove_backends(void); +extern int mdb_set_default_backend(MdbHandle *mdb, char *backend_name); +extern char *mdb_get_relationships(MdbHandle *mdb); + +/* sargs.c */ +extern int mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields); +extern int mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field); +extern void mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data); +extern int mdb_find_indexable_sargs(MdbSargNode *node, gpointer data); +extern int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg); +extern int mdb_test_string(MdbSargNode *node, char *s); +extern int mdb_test_int(MdbSargNode *node, gint32 i); +extern int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg); + + + +/* index.c */ +extern GPtrArray *mdb_read_indices(MdbTableDef *table); +extern void mdb_index_dump(MdbTableDef *table, MdbIndex *idx); +extern void mdb_index_scan_free(MdbTableDef *table); +extern int mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg); +extern int mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row); +extern void mdb_index_hash_text(guchar *text, guchar *hash); +extern void mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table); +extern int mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row); +extern void mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest); +extern void mdb_free_indices(GPtrArray *indices); +void mdb_index_page_reset(MdbIndexPage *ipg); +extern MdbIndexPage *mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain); +extern MdbIndexPage *mdb_index_unwind(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain); +extern void mdb_index_page_init(MdbIndexPage *ipg); + + +/* stats.c */ +extern void mdb_stats_on(MdbHandle *mdb); +extern void mdb_stats_off(MdbHandle *mdb); +extern void mdb_dump_stats(MdbHandle *mdb); + +/* like.c */ +extern int mdb_like_cmp(char *s, char *r); + +/* write.c */ +extern int mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields); +extern guint16 mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size); +extern int mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum); +extern int mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields); +extern int mdb_replace_row(MdbTableDef *table, int row, unsigned char *new_row, int new_row_size); +extern int mdb_pg_get_freespace(MdbHandle *mdb); +extern int mdb_update_row(MdbTableDef *table); +extern unsigned char *mdb_new_data_pg(MdbCatalogEntry *entry); + +/* map.c */ +extern guint32 mdb_map_find_next_freepage(MdbTableDef *table, int row_size); +extern guint32 mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg); + +/* props.c */ +extern GPtrArray *mdb_read_props_list(gchar *kkd, int len); +extern void mdb_free_props(MdbProperties *props); +extern MdbProperties *mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len); + +/* worktable.c */ +extern MdbTableDef *mdb_create_temp_table(MdbHandle *mdb, char *name); +extern void mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col); +extern void mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed); +extern void mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int column); +extern void mdb_temp_columns_end(MdbTableDef *table); + +/* options.c */ +extern int mdb_get_option(unsigned long optnum); +extern void mdb_debug(int klass, char *fmt, ...); + +/* iconv.c */ +extern int mdb_unicode2ascii(MdbHandle *mdb, unsigned char *buf, int offset, unsigned int len, char *dest); +extern int mdb_ascii2unicode(MdbHandle *mdb, unsigned char *buf, int offset, unsigned int len, char *dest); + +#ifdef __cplusplus + } +#endif + +#endif /* _mdbtools_h_ */ diff --git a/map/poi_geodownload/libmdb/index.c b/map/poi_geodownload/libmdb/index.c new file mode 100644 index 00000000..6da04c47 --- /dev/null +++ b/map/poi_geodownload/libmdb/index.c @@ -0,0 +1,907 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000-2004 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +MdbIndexPage *mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain); +MdbIndexPage *mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg); + +char idx_to_text[] = { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0-7 0x00-0x07 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8-15 0x09-0x0f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16-23 0x10-0x17 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24-31 0x19-0x1f */ +' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 32-39 0x20-0x27 */ +0x00, 0x00, 0x00, 0x00, 0x00, ' ', ' ', 0x00, /* 40-47 0x29-0x2f */ +'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', /* 48-55 0x30-0x37 */ +'^', '_', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56-63 0x39-0x3f */ +0x00, '`', 'a', 'b', 'd', 'f', 'g', 'h', /* 64-71 0x40-0x47 */ +'i', 'j', 'k', 'l', 'm', 'o', 'p', 'r', /* 72-79 0x49-0x4f H */ +'s', 't', 'u', 'v', 'w', 'x', 'z', '{', /* 80-87 0x50-0x57 P */ +'|', '}', '~', '5', '6', '7', '8', '9', /* 88-95 0x59-0x5f */ +0x00, '`', 'a', 'b', 'd', 'f', 'g', 'h', /* 96-103 0x60-0x67 */ +'i', 'j', 'k', 'l', 'm', 'o', 'p', 'r', /* 014-111 0x69-0x6f h */ +'s', 't', 'u', 'v', 'w', 'x', 'z', '{', /* 112-119 0x70-0x77 p */ +'|', '}', '~', 0x00, 0x00, 0x00, 0x00, 0x00, /* 120-127 0x78-0x7f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128-135 0x80-0x87 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */ +0x00, 0x00, 0x00, 0x00, 0x00, '`', 0x00, 0x00, /* 0xc0-0xc7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */ +0x00, '`', 0x00, '`', '`', '`', 0x00, 0x00, /* 0xe0-0xe7 */ +'f', 'f', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */ +0x00, 0x00, 0x00, 'r', 0x00, 0x00, 'r', 0x00, /* 0xf0-0xf7 */ +0x81, 0x00, 0x00, 0x00, 'x', 0x00, 0x00, 0x00, /* 0xf8-0xff */ +}; + + +GPtrArray * +mdb_read_indices(MdbTableDef *table) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + MdbIndex *pidx; + unsigned int i, j; + int idx_num, key_num, col_num; + int cur_pos, name_sz, idx2_sz, type_offset; + int index_start_pg = mdb->cur_pg; + guchar *tmpbuf; + + table->indices = g_ptr_array_new(); + + if (IS_JET4(mdb)) { + cur_pos = table->index_start + 52 * table->num_real_idxs; + idx2_sz = 28; + type_offset = 23; + } else { + cur_pos = table->index_start + 39 * table->num_real_idxs; + idx2_sz = 20; + type_offset = 19; + } + + tmpbuf = (guchar *) g_malloc(idx2_sz); + for (i=0;i<table->num_idxs;i++) { + read_pg_if_n(mdb, tmpbuf, &cur_pos, idx2_sz); + cur_pos += idx2_sz; + pidx = (MdbIndex *) g_malloc0(sizeof(MdbIndex)); + pidx->table = table; + pidx->index_num = mdb_get_int16(tmpbuf, 4); + pidx->index_type = tmpbuf[type_offset]; + g_ptr_array_add(table->indices, pidx); + } + g_free(tmpbuf); + + for (i=0;i<table->num_idxs;i++) { + pidx = g_ptr_array_index (table->indices, i); + if (IS_JET4(mdb)) { + name_sz=read_pg_if_16(mdb, &cur_pos); + cur_pos += 2; + tmpbuf = g_malloc(name_sz); + read_pg_if_n(mdb, tmpbuf, &cur_pos, name_sz); + cur_pos += name_sz; + mdb_unicode2ascii(mdb, tmpbuf, 0, name_sz, pidx->name); + g_free(tmpbuf); + } else { + read_pg_if(mdb, &cur_pos, 0); + name_sz=mdb->pg_buf[cur_pos++]; + read_pg_if_n(mdb, (unsigned char *) pidx->name, &cur_pos, name_sz); + cur_pos += name_sz; + pidx->name[name_sz]='\0'; + } + //fprintf(stderr, "index name %s\n", pidx->name); + } + + mdb_read_alt_pg(mdb, entry->table_pg); + mdb_read_pg(mdb, index_start_pg); + cur_pos = table->index_start; + idx_num=0; + for (i=0;i<table->num_real_idxs;i++) { + if (IS_JET4(mdb)) cur_pos += 4; + do { + pidx = g_ptr_array_index (table->indices, idx_num++); + } while (pidx && pidx->index_type==2); + + /* if there are more real indexes than index entries left after + removing type 2's decrement real indexes and continue. Happens + on Northwind Orders table. + */ + if (!pidx) { + table->num_real_idxs--; + continue; + } + + pidx->num_rows = mdb_get_int32(mdb->alt_pg_buf, + fmt->tab_cols_start_offset + + (i*fmt->tab_ridx_entry_size)); + + key_num=0; + for (j=0;j<MDB_MAX_IDX_COLS;j++) { + col_num=read_pg_if_16(mdb,&cur_pos); + cur_pos += 2; + read_pg_if(mdb, &cur_pos, 0); + cur_pos++; + if (col_num == 0xFFFF) + continue; + /* set column number to a 1 based column number and store */ + pidx->key_col_num[key_num] = col_num + 1; + pidx->key_col_order[key_num] = + (mdb->pg_buf[cur_pos-1]) ? MDB_ASC : MDB_DESC; + key_num++; + } + pidx->num_keys = key_num; + + cur_pos += 4; + pidx->first_pg = read_pg_if_32(mdb, &cur_pos); + cur_pos += 4; + read_pg_if(mdb, &cur_pos, 0); + pidx->flags = mdb->pg_buf[cur_pos++]; + if (IS_JET4(mdb)) cur_pos += 9; + } + return NULL; +} +void +mdb_index_hash_text(guchar *text, guchar *hash) +{ + unsigned int k; + + for (k=0;k<strlen((char*) text);k++) { + hash[k] = idx_to_text[text[k]]; + if (!(hash[k])) fprintf(stderr, + "No translation available for %02x %d\n", + text[k],text[k]); + } + hash[strlen((char*) text)]=0; +} +void +mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest) +{ + int i, j = 0; + + for (i = sz; i > 0; i--) { + dest[j++] = src[i]; + } +} +static void +mdb_index_cache_sarg(MdbColumn *col, MdbSarg *sarg, MdbSarg *idx_sarg) +{ + //guint32 cache_int; + unsigned char *c; + + switch (col->col_type) { + case MDB_TEXT: + mdb_index_hash_text((unsigned char*) sarg->value.s, (unsigned char*) idx_sarg->value.s); + break; + + case MDB_LONGINT: + idx_sarg->value.i = GUINT32_SWAP_LE_BE(sarg->value.i); + //cache_int = sarg->value.i * -1; + c = (unsigned char *) &(idx_sarg->value.i); + c[0] |= 0x80; + //printf("int %08x %02x %02x %02x %02x\n", sarg->value.i, c[0], c[1], c[2], c[3]); + break; + + case MDB_INT: + break; + + default: + break; + } +} +#if 0 +int +mdb_index_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSarg *sarg, int offset, int len) +{ +char tmpbuf[256]; +int lastchar; + + switch (col->col_type) { + case MDB_BYTE: + return mdb_test_int(sarg, mdb_pg_get_byte(mdb, offset)); + break; + case MDB_INT: + return mdb_test_int(sarg, mdb_pg_get_int16(mdb, offset)); + break; + case MDB_LONGINT: + return mdb_test_int(sarg, mdb_pg_get_int32(mdb, offset)); + break; + case MDB_TEXT: + strncpy(tmpbuf, &mdb->pg_buf[offset],255); + lastchar = len > 255 ? 255 : len; + tmpbuf[lastchar]='\0'; + return mdb_test_string(sarg, tmpbuf); + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type); + break; + } + return 1; +} +#endif +static int +mdb_index_test_sargs(MdbHandle *mdb, MdbIndex *idx, unsigned char *buf, int len) +{ + unsigned int i, j; + MdbColumn *col; + MdbTableDef *table = idx->table; + MdbSarg *idx_sarg; + MdbSarg *sarg; + MdbField field; + MdbSargNode node; + //int c_offset = 0, + int c_len; + +#if 0 + fprintf(stderr,"mdb_index_test_sargs called on "); + for (i=0;i<len;i++) + fprintf(stderr,"%02x ",buf[i]); //mdb->pg_buf[offset+i]); + fprintf(stderr,"\n"); +#endif + for (i=0;i<idx->num_keys;i++) { + //c_offset++; /* the per column null indicator/flags */ + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + /* + * This will go away eventually + */ + if (col->col_type==MDB_TEXT) { + //c_len = strlen(&mdb->pg_buf[offset + c_offset]); + c_len = strlen((char*) buf); + } else { + c_len = col->col_size; + //fprintf(stderr,"Only text types currently supported. How did we get here?\n"); + } + /* + * If we have no cached index values for this column, + * create them. + */ + if (col->num_sargs && !col->idx_sarg_cache) { + col->idx_sarg_cache = g_ptr_array_new(); + for (j=0;j<col->num_sargs;j++) { + sarg = g_ptr_array_index (col->sargs, j); + idx_sarg = g_memdup(sarg,sizeof(MdbSarg)); + //printf("calling mdb_index_cache_sarg\n"); + mdb_index_cache_sarg(col, sarg, idx_sarg); + g_ptr_array_add(col->idx_sarg_cache, idx_sarg); + } + } + + for (j=0;j<col->num_sargs;j++) { + sarg = g_ptr_array_index (col->idx_sarg_cache, j); + /* XXX - kludge */ + node.op = sarg->op; + node.value = sarg->value; + //field.value = &mdb->pg_buf[offset + c_offset]; + field.value = buf; + field.siz = c_len; + field.is_null = FALSE; + if (!mdb_test_sarg(mdb, col, &node, &field)) { + /* sarg didn't match, no sense going on */ + return 0; + } + } + } + return 1; +} +#if 0 +/* + * pack the pages bitmap + */ +int +mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg) +{ + int mask_bit = 0; + int mask_pos = 0x16; + int mask_byte = 0; + int elem = 0; + int len, start, i; + + start = ipg->idx_starts[elem++]; + + while (start) { + len = ipg->idx_starts[elem] - start; + fprintf(stdout, "len is %d\n", len); + for (i=0; i < len; i++) { + mask_bit++; + if (mask_bit==8) { + mask_bit=0; + mdb->pg_buf[mask_pos++] = mask_byte; + mask_byte = 0; + } + /* upon reaching the len, set the bit */ + } + mask_byte = (1 << mask_bit) | mask_byte; + fprintf(stdout, "mask byte is %02x at %d\n", mask_byte, mask_pos); + start = ipg->idx_starts[elem++]; + } + /* flush the last byte if any */ + mdb->pg_buf[mask_pos++] = mask_byte; + /* remember to zero the rest of the bitmap */ + for (i = mask_pos; i < 0xf8; i++) { + mdb->pg_buf[mask_pos++] = 0; + } + return 0; +} +#endif +/* + * unpack the pages bitmap + */ +static int +mdb_index_unpack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg) +{ + int mask_bit = 0; + int mask_pos = 0x16; + int mask_byte; + int start = 0xf8; + int elem = 0; + int len = 0; + + ipg->idx_starts[elem++]=start; + +#if 0 + fprintf(stdout, "Unpacking index page %u\n", ipg->pg); +#endif + do { + len = 0; + do { + mask_bit++; + if (mask_bit==8) { + mask_bit=0; + mask_pos++; + } + mask_byte = mdb->pg_buf[mask_pos]; + len++; + } while (mask_pos <= 0xf8 && !((1 << mask_bit) & mask_byte)); + //fprintf(stdout, "%d %d %d %d\n", mask_pos, mask_bit, mask_byte, len); + + start += len; + if (mask_pos < 0xf8) ipg->idx_starts[elem++]=start; + + } while (mask_pos < 0xf8); + + /* if we zero the next element, so we don't pick up the last pages starts*/ + ipg->idx_starts[elem]=0; + + return elem; +} +/* + * find the next entry on a page (either index or leaf). Uses state information + * stored in the MdbIndexPage across calls. + */ +int +mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg) +{ + if (!ipg->pg) return 0; + + /* if this page has not been unpacked to it */ + if (!ipg->idx_starts[0]){ + //fprintf(stdout, "Unpacking page %d\n", ipg->pg); + mdb_index_unpack_bitmap(mdb, ipg); + } + + + if (ipg->idx_starts[ipg->start_pos + 1]==0) return 0; + ipg->len = ipg->idx_starts[ipg->start_pos+1] - ipg->idx_starts[ipg->start_pos]; + ipg->start_pos++; + //fprintf(stdout, "Start pos %d\n", ipg->start_pos); + + return ipg->len; +} +void mdb_index_page_reset(MdbIndexPage *ipg) +{ + ipg->offset = 0xf8; /* start byte of the index entries */ + ipg->start_pos=0; + ipg->len = 0; + ipg->idx_starts[0]=0; +} +void mdb_index_page_init(MdbIndexPage *ipg) +{ + memset(ipg, 0, sizeof(MdbIndexPage)); + mdb_index_page_reset(ipg); +} +/* + * find the next leaf page if any given a chain. Assumes any exhausted leaf + * pages at the end of the chain have been peeled off before the call. + */ +static MdbIndexPage * +mdb_find_next_leaf(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg, *newipg; + guint32 pg; + guint passed = 0; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + + /* + * If we are at the first page deep and it's not an index page then + * we are simply done. (there is no page to find + */ + + if (mdb->pg_buf[0]==MDB_PAGE_LEAF) { + /* Indexes can have leaves at the end that don't appear + * in the upper tree, stash the last index found so + * we can follow it at the end. */ + chain->last_leaf_found = ipg->pg; + return ipg; + } + + /* + * apply sargs here, currently we don't + */ + do { + ipg->len = 0; + //printf("finding next on pg %lu\n", ipg->pg); + if (!mdb_index_find_next_on_page(mdb, ipg)) { + //printf("find_next_on_page returned 0\n"); + return 0; + } + pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 3); + //printf("Looking at pg %lu at %lu %d\n", pg, ipg->offset, ipg->len); + ipg->offset += ipg->len; + + /* + * add to the chain and call this function + * recursively. + */ + newipg = mdb_chain_add_page(mdb, chain, pg); + newipg = mdb_find_next_leaf(mdb, idx, chain); + //printf("returning pg %lu\n",newipg->pg); + return newipg; + } while (!passed); + /* no more pages */ + return NULL; + +} +MdbIndexPage * +mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg) +{ + MdbIndexPage *ipg; + + chain->cur_depth++; + if (chain->cur_depth > MDB_MAX_INDEX_DEPTH) { + fprintf(stderr,"Error! maximum index depth of %d exceeded. This is probably due to a programming bug, If you are confident that your indexes really are this deep, adjust MDB_MAX_INDEX_DEPTH in mdbtools.h and recompile.\n", MDB_MAX_INDEX_DEPTH); + exit(1); + } + ipg = &(chain->pages[chain->cur_depth - 1]); + mdb_index_page_init(ipg); + ipg->pg = pg; + + return ipg; +} +/* + * returns the bottom page of the IndexChain, if IndexChain is empty it + * initializes it by reading idx->first_pg (the root page) + */ +MdbIndexPage * +mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg; + + /* + * if it's new use the root index page (idx->first_pg) + */ + if (!chain->cur_depth) { + ipg = &(chain->pages[0]); + mdb_index_page_init(ipg); + chain->cur_depth = 1; + ipg->pg = idx->first_pg; + if (!(ipg = mdb_find_next_leaf(mdb, idx, chain))) + return 0; + } else { + ipg = &(chain->pages[chain->cur_depth - 1]); + ipg->len = 0; + } + + mdb_read_pg(mdb, ipg->pg); + + return ipg; +} +/* + * unwind the stack and search for new leaf node + */ +MdbIndexPage * +mdb_index_unwind(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg; + + //printf("page %lu finished\n",ipg->pg); + if (chain->cur_depth==1) { + //printf("cur_depth == 1 we're out\n"); + return NULL; + } + /* + * unwind the stack until we find something or reach + * the top. + */ + ipg = NULL; + while (chain->cur_depth>1 && ipg==NULL) { + //printf("chain depth %d\n", chain->cur_depth); + chain->cur_depth--; + ipg = mdb_find_next_leaf(mdb, idx, chain); + if (ipg) mdb_index_find_next_on_page(mdb, ipg); + } + if (chain->cur_depth==1) { + //printf("last leaf %lu\n", chain->last_leaf_found); + return NULL; + } + return ipg; +} +/* + * the main index function. + * caller provides an index chain which is the current traversal of index + * pages from the root page to the leaf. Initially passed as blank, + * mdb_index_find_next will store it's state information here. Each invocation + * then picks up where the last one left off, allowing us to scroll through + * the index one by one. + * + * Sargs are applied here but also need to be applied on the whole row b/c + * text columns may return false positives due to hashing and non-index + * columns with sarg values can't be tested here. + */ +int +mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row) +{ + MdbIndexPage *ipg; + int passed = 0; + int idx_sz; + int idx_start = 0; + MdbColumn *col; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + + /* + * loop while the sargs don't match + */ + do { + ipg->len = 0; + /* + * if no more rows on this leaf, try to find a new leaf + */ + if (!mdb_index_find_next_on_page(mdb, ipg)) { + if (!chain->clean_up_mode) { + if (!(ipg = mdb_index_unwind(mdb, idx, chain))) + chain->clean_up_mode = 1; + } + if (chain->clean_up_mode) { + //fprintf(stdout,"in cleanup mode\n"); + + if (!chain->last_leaf_found) return 0; + mdb_read_pg(mdb, chain->last_leaf_found); + chain->last_leaf_found = mdb_pg_get_int24(mdb, 0x0c); + //printf("next leaf %lu\n", chain->last_leaf_found); + mdb_read_pg(mdb, chain->last_leaf_found); + /* reuse the chain for cleanup mode */ + chain->cur_depth = 1; + ipg = &chain->pages[0]; + mdb_index_page_init(ipg); + ipg->pg = chain->last_leaf_found; + //printf("next on page %d\n", + if (!mdb_index_find_next_on_page(mdb, ipg)) + return 0; + } + } + *row = mdb->pg_buf[ipg->offset + ipg->len - 1]; +#if 0 + printf("page: "); + buffer_dump(mdb->pg_buf, ipg->offset+ipg->len-4, ipg->offset+ipg->len-2); +#endif + *pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4); +#if 0 + printf("row = %d pg = %lu ipg->pg = %lu offset = %lu len = %d\n", *row, *pg, ipg->pg, ipg->offset, ipg->len); +#endif + col=g_ptr_array_index(idx->table->columns,idx->key_col_num[0]-1); + idx_sz = mdb_col_fixed_size(col); + /* handle compressed indexes, single key indexes only? */ + if (idx->num_keys==1 && idx_sz>0 && ipg->len - 4 < idx_sz) { +#if 0 + printf("short index found\n"); + buffer_dump(ipg->cache_value, 0, idx_sz); +#endif + memcpy(&ipg->cache_value[idx_sz - (ipg->len - 4)], &mdb->pg_buf[ipg->offset], ipg->len); +#if 0 + buffer_dump(ipg->cache_value, 0, idx_sz); +#endif + } else { + idx_start = ipg->offset + (ipg->len - 4 - idx_sz); + memcpy(ipg->cache_value, &mdb->pg_buf[idx_start], idx_sz); + } + + //idx_start = ipg->offset + (ipg->len - 4 - idx_sz); + passed = mdb_index_test_sargs(mdb, idx, ipg->cache_value, idx_sz); + +// printf("passed=%d\n", passed); + + buffer_dump(mdb->pg_buf, ipg->offset, ipg->offset+ipg->len-1); + ipg->offset += ipg->len; + + } while (!passed); + +#if 0 + fprintf(stdout,"len = %d pos %d\n", ipg->len, ipg->len); + buffer_dump(mdb->pg_buf, ipg->offset, ipg->offset+ipg->len-1); +#endif + + return ipg->len; +} +/* + * XXX - FIX ME + * This function is grossly inefficient. It scans the entire index building + * an IndexChain to a specific row. We should be checking the index pages + * for matches against the indexed fields to find the proper leaf page, but + * getting it working first and then make it fast! + */ +int +mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row) +{ + MdbIndexPage *ipg; + int passed = 0; + guint32 datapg; + guint16 datarow; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + + do { + ipg->len = 0; + /* + * if no more rows on this leaf, try to find a new leaf + */ + if (!mdb_index_find_next_on_page(mdb, ipg)) { + /* back to top? We're done */ + if (chain->cur_depth==1) + return 0; + + /* + * unwind the stack until we find something or reach + * the top. + */ + while (chain->cur_depth>1) { + chain->cur_depth--; + if (!(ipg = mdb_find_next_leaf(mdb, idx, chain))) + return 0; + mdb_index_find_next_on_page(mdb, ipg); + } + if (chain->cur_depth==1) + return 0; + } + /* test row and pg */ + datarow = mdb->pg_buf[ipg->offset + ipg->len - 1]; + datapg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4); + + if (datapg == pg && datarow == row) { + passed = 1; + } + ipg->offset += ipg->len; + } while (!passed); + + /* index chain from root to leaf should now be in "chain" */ + return 1; +} + +static void mdb_index_walk(MdbTableDef *table, MdbIndex *idx) +{ +MdbHandle *mdb = table->entry->mdb; +int cur_pos = 0; +unsigned char marker; +MdbColumn *col; +unsigned int i; + + if (idx->num_keys!=1) return; + + mdb_read_pg(mdb, idx->first_pg); + cur_pos = 0xf8; + + for (i=0;i<idx->num_keys;i++) { + marker = mdb->pg_buf[cur_pos++]; + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + //printf("column %d coltype %d col_size %d (%d)\n",i,col->col_type, mdb_col_fixed_size(col), col->col_size); + } +} +void +mdb_index_dump(MdbTableDef *table, MdbIndex *idx) +{ + unsigned int i; + MdbColumn *col; + + fprintf(stdout,"index number %d\n", idx->index_num); + fprintf(stdout,"index name %s\n", idx->name); + fprintf(stdout,"index first page %d\n", idx->first_pg); + fprintf(stdout,"index rows %d\n", idx->num_rows); + if (idx->index_type==1) fprintf(stdout,"index is a primary key\n"); + for (i=0;i<idx->num_keys;i++) { + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + fprintf(stdout,"Column %s(%d) Sorted %s Unique: %s\n", + col->name, + idx->key_col_num[i], + idx->key_col_order[i]==MDB_ASC ? "ascending" : "descending", + idx->flags & MDB_IDX_UNIQUE ? "Yes" : "No" + ); + } + mdb_index_walk(table, idx); +} +/* + * compute_cost tries to assign a cost to a given index using the sargs + * available in this query. + * + * Indexes with no matching sargs are assigned 0 + * Unique indexes are preferred over non-uniques + * Operator preference is equal, like, isnull, others + */ +static int mdb_index_compute_cost(MdbTableDef *table, MdbIndex *idx) +{ + unsigned int i; + MdbColumn *col; + MdbSarg *sarg = NULL; + int not_all_equal = 0; + + if (!idx->num_keys) return 0; + if (idx->num_keys > 1) { + for (i=0;i<idx->num_keys;i++) { + col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1); + if (col->sargs) sarg = g_ptr_array_index (col->sargs, 0); + if (!sarg || sarg->op != MDB_EQUAL) not_all_equal++; + } + } + + col=g_ptr_array_index(table->columns,idx->key_col_num[0]-1); + /* + * if this is the first key column and there are no sargs, + * then this index is useless. + */ + if (!col->num_sargs) return 0; + + sarg = g_ptr_array_index (col->sargs, 0); + + /* + * a like with a wild card first is useless as a sarg */ + if (sarg->op == MDB_LIKE && sarg->value.s[0]=='%') + return 0; + + /* + * this needs a lot of tweaking. + */ + if (idx->flags & MDB_IDX_UNIQUE) { + if (idx->num_keys == 1) { + //printf("op is %d\n", sarg->op); + switch (sarg->op) { + case MDB_EQUAL: + return 1; break; + case MDB_LIKE: + return 4; break; + case MDB_ISNULL: + return 12; break; + default: + return 8; break; + } + } else { + switch (sarg->op) { + case MDB_EQUAL: + if (not_all_equal) return 2; + else return 1; + break; + case MDB_LIKE: + return 6; break; + case MDB_ISNULL: + return 12; break; + default: + return 9; break; + } + } + } else { + if (idx->num_keys == 1) { + switch (sarg->op) { + case MDB_EQUAL: + return 2; break; + case MDB_LIKE: + return 5; break; + case MDB_ISNULL: + return 12; break; + default: + return 10; break; + } + } else { + switch (sarg->op) { + case MDB_EQUAL: + if (not_all_equal) return 3; + else return 2; + break; + case MDB_LIKE: + return 7; break; + case MDB_ISNULL: + return 12; break; + default: + return 11; break; + } + } + } + return 0; +} +/* + * choose_index runs mdb_index_compute_cost for each available index and picks + * the best. + * + * Returns strategy to use (table scan, or index scan) + */ +static MdbStrategy +mdb_choose_index(MdbTableDef *table, int *choice) +{ + unsigned int i; + MdbIndex *idx; + int cost = 0; + int least = 99; + + *choice = -1; + for (i=0;i<table->num_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + cost = mdb_index_compute_cost(table, idx); + //printf("cost for %s is %d\n", idx->name, cost); + if (cost && cost < least) { + least = cost; + *choice = i; + } + } + /* and the winner is: *choice */ + if (least==99) return MDB_TABLE_SCAN; + return MDB_INDEX_SCAN; +} +void +mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table) +{ + int i; + + if (mdb_get_option(MDB_USE_INDEX) && mdb_choose_index(table, &i) == MDB_INDEX_SCAN) { + table->strategy = MDB_INDEX_SCAN; + table->scan_idx = g_ptr_array_index (table->indices, i); + table->chain = g_malloc0(sizeof(MdbIndexChain)); + table->mdbidx = mdb_clone_handle(mdb); + mdb_read_pg(table->mdbidx, table->scan_idx->first_pg); + //printf("best index is %s\n",table->scan_idx->name); + } + //printf("TABLE SCAN? %d\n", table->strategy); +} +void +mdb_index_scan_free(MdbTableDef *table) +{ + if (table->chain) { + g_free(table->chain); + table->chain = NULL; + } + if (table->mdbidx) { + mdb_close(table->mdbidx); + table->mdbidx = NULL; + } +} + +void mdb_free_indices(GPtrArray *indices) +{ + unsigned int i; + + if (!indices) return; + for (i=0; i<indices->len; i++) + g_free (g_ptr_array_index(indices, i)); + g_ptr_array_free(indices, TRUE); +} diff --git a/map/poi_geodownload/libmdb/kkd.c b/map/poi_geodownload/libmdb/kkd.c new file mode 100644 index 00000000..acf38be8 --- /dev/null +++ b/map/poi_geodownload/libmdb/kkd.c @@ -0,0 +1,151 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +/* +** Note: This code is mostly garbage right now...just a test to parse out the +** KKD structures. +*/ + +#if 0 +static GArray *mdb_get_column_props(MdbCatalogEntry *entry, int start) +{ +int pos, cnt=0; +int len, tmp, cplen; +MdbColumnProp prop; +MdbHandle *mdb = entry->mdb; + + entry->props = g_array_new(FALSE,FALSE,sizeof(MdbColumnProp)); + len = mdb_pg_get_int16(mdb,start); + pos = start + 6; + while (pos < start+len) { + tmp = mdb_pg_get_int16(mdb,pos); /* length of string */ + pos += 2; + cplen = tmp > MDB_MAX_OBJ_NAME ? MDB_MAX_OBJ_NAME : tmp; + g_memmove(prop.name,&mdb->pg_buf[pos],cplen); + prop.name[cplen]='\0'; + pos += tmp; + g_array_append_val(entry->props, prop.name); + cnt++; + } + entry->num_props = cnt; + return entry->props; +} + +static GHashTable *mdb_get_column_def(MdbCatalogEntry *entry, int start) +{ +GHashTable *hash = NULL; +MdbHandle *mdb = entry->mdb; +MdbColumnProp prop; +int tmp, pos, col_num, val_len, i; +int len, col_type; +unsigned char c; +int end; + + fprintf(stdout,"\n data\n"); + fprintf(stdout,"-------\n"); + len = mdb_pg_get_int16(mdb,start); + fprintf(stdout,"length = %3d\n",len); + pos = start + 6; + end = start + len; + while (pos < end) { + fprintf(stdout,"pos = %3d\n",pos); + start = pos; + tmp = mdb_pg_get_int16(mdb,pos); /* length of field */ + pos += 2; + col_type = mdb_pg_get_int16(mdb,pos); /* ??? */ + pos += 2; + col_num = 0; + if (col_type) { + col_num = mdb_pg_get_int16(mdb,pos); + pos += 2; + } + val_len = mdb_pg_get_int16(mdb,pos); + pos += 2; + fprintf(stdout,"length = %3d %04x %2d %2d ",tmp, col_type, col_num, val_len); + for (i=0;i<val_len;i++) { + c = mdb->pg_buf[pos+i]; + if (isprint(c)) + fprintf(stdout," %c",c); + else + fprintf(stdout," %02x",c); + + } + pos = start + tmp; + prop = g_array_index(entry->props,MdbColumnProp,col_num); + fprintf(stdout," Property %s",prop.name); + fprintf(stdout,"\n"); + } + return hash; +} + +static void mdb_kkd_dump(MdbCatalogEntry *entry) +{ +int rows; +int kkd_start, kkd_end; +int i, tmp, pos, row_type, datapos=0; +MdbColumnProp prop; +MdbHandle *mdb = entry->mdb; +int rowid = entry->kkd_rowid; + + + mdb_read_pg(mdb, entry->kkd_pg); + rows = mdb_pg_get_int16(mdb,8); + fprintf(stdout,"number of rows = %d\n",rows); + kkd_start = mdb_pg_get_int16(mdb,10+rowid*2); + fprintf(stdout,"kkd start = %d %04x\n",kkd_start,kkd_start); + kkd_end = mdb->fmt->pg_size; + for (i=0;i<rows;i++) { + tmp = mdb_pg_get_int16(mdb, 10+i*2); + if (tmp < mdb->fmt->pg_size && + tmp > kkd_start && + tmp < kkd_end) { + kkd_end = tmp; + } + } + fprintf(stdout,"kkd end = %d %04x\n",kkd_end,kkd_end); + pos = kkd_start + 4; /* 4 = K K D \0 */ + while (pos < kkd_end) { + tmp = mdb_pg_get_int16(mdb,pos); + row_type = mdb_pg_get_int16(mdb,pos+4); + fprintf(stdout,"row size = %3d type = 0x%02x\n",tmp,row_type); + if (row_type==0x80) { + fprintf(stdout,"\nColumn Properties\n"); + fprintf(stdout,"-----------------\n"); + mdb_get_column_props(entry,pos); + for (i=0;i<entry->num_props;i++) { + prop = g_array_index(entry->props,MdbColumnProp,i); + fprintf(stdout,"%3d %s\n",i,prop.name); + } + } + if (row_type==0x01) datapos = pos; + pos += tmp; + } + + if (datapos) { + mdb_get_column_def(entry, datapos); + } +} +#endif diff --git a/map/poi_geodownload/libmdb/like.c b/map/poi_geodownload/libmdb/like.c new file mode 100644 index 00000000..0a23d45c --- /dev/null +++ b/map/poi_geodownload/libmdb/like.c @@ -0,0 +1,78 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <string.h> +#include <mdbtools.h> + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/** + * mdb_like_cmp + * @s: String to search within. + * @r: Search pattern. + * + * Tests the string @s to see if it matches the search pattern @r. In the + * search pattern, a percent sign indicates matching on any number of + * characters, and an underscore indicates matching any single character. + * + * Returns: 1 if the string matches, 0 if the string does not match. + */ +int mdb_like_cmp(char *s, char *r) +{ + unsigned int i; + int ret; + + mdb_debug(MDB_DEBUG_LIKE, "comparing %s and %s", s, r); + switch (r[0]) { + case '\0': + if (s[0]=='\0') { + return 1; + } else { + return 0; + } + case '_': + /* skip one character */ + return mdb_like_cmp(&s[1],&r[1]); + case '%': + /* skip any number of characters */ + /* the strlen(s)+1 is important so the next call can */ + /* if there are trailing characters */ + for(i=0;i<strlen(s)+1;i++) { + if (mdb_like_cmp(&s[i],&r[1])) { + return 1; + } + } + return 0; + default: + for(i=0;i<strlen(r);i++) { + if (r[i]=='_' || r[i]=='%') break; + } + if (strncmp(s,r,i)) { + return 0; + } else { + mdb_debug(MDB_DEBUG_LIKE, "at pos %d comparing %s and %s", i, &s[i], &r[i]); + ret = mdb_like_cmp(&s[i],&r[i]); + mdb_debug(MDB_DEBUG_LIKE, "returning %d (%s and %s)", ret, &s[i], &r[i]); + return ret; + } + } +} diff --git a/map/poi_geodownload/libmdb/map.c b/map/poi_geodownload/libmdb/map.c new file mode 100644 index 00000000..aee66269 --- /dev/null +++ b/map/poi_geodownload/libmdb/map.c @@ -0,0 +1,133 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +static guint32 +mdb_map_find_next0(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg) +{ + guint32 pgnum, i, usage_bitlen; + unsigned char *usage_bitmap; + + pgnum = mdb_get_int32(map, 1); + usage_bitmap = map + 5; + usage_bitlen = (map_sz - 5) * 8; + + i = (start_pg >= pgnum) ? start_pg-pgnum+1 : 0; + for (; i<usage_bitlen; i++) { + if (usage_bitmap[i/8] & (1 << (i%8))) { + return pgnum + i; + } + } + /* didn't find anything */ + return 0; +} +static int +mdb_map_find_next1(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg) +{ + guint32 map_ind, max_map_pgs, offset, usage_bitlen; + + /* + * start_pg will tell us where to (re)start the scan + * for the next data page. each usage_map entry points to a + * 0x05 page which bitmaps (mdb->fmt->pg_size - 4) * 8 pages. + * + * map_ind gives us the starting usage_map entry + * offset gives us a page offset into the bitmap + */ + usage_bitlen = (mdb->fmt->pg_size - 4) * 8; + max_map_pgs = (map_sz - 1) / 4; + map_ind = (start_pg + 1) / usage_bitlen; + offset = (start_pg + 1) % usage_bitlen; + + for (; map_ind<max_map_pgs; map_ind++) { + unsigned char *usage_bitmap; + guint32 i, map_pg; + + if (!(map_pg = mdb_get_int32(map, (map_ind*4)+1))) { + continue; + } + if(mdb_read_alt_pg(mdb, map_pg) != mdb->fmt->pg_size) { + fprintf(stderr, "Oops! didn't get a full page at %d\n", map_pg); + exit(1); + } + + usage_bitmap = mdb->alt_pg_buf + 4; + for (i=offset; i<usage_bitlen; i++) { + if (usage_bitmap[i/8] & (1 << (i%8))) { + return map_ind*usage_bitlen + i; + } + } + offset = 0; + } + /* didn't find anything */ + return 0; +} +guint32 +mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg) +{ + if (map[0] == 0) { + return mdb_map_find_next0(mdb, map, map_sz, start_pg); + } else if (map[0] == 1) { + return mdb_map_find_next1(mdb, map, map_sz, start_pg); + } + + fprintf(stderr, "Warning: unrecognized usage map type: %d\n", map[0]); + return -1; +} +static guint32 +mdb_alloc_page(MdbTableDef *table) +{ + printf("Allocating new page\n"); + return 0; +} +guint32 +mdb_map_find_next_freepage(MdbTableDef *table, int row_size) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + guint32 pgnum; + guint32 cur_pg = 0; + int free_space; + + do { + pgnum = mdb_map_find_next(mdb, + table->free_usage_map, + table->freemap_sz, cur_pg); + printf("looking at page %d\n", pgnum); + if (!pgnum) { + /* allocate new page */ + pgnum = mdb_alloc_page(table); + return pgnum; + } + cur_pg = pgnum; + + mdb_read_pg(mdb, pgnum); + free_space = mdb_pg_get_freespace(mdb); + + } while (free_space < row_size); + + printf("page %d has %d bytes left\n", pgnum, free_space); + + return pgnum; +} diff --git a/map/poi_geodownload/libmdb/mem.c b/map/poi_geodownload/libmdb/mem.c new file mode 100644 index 00000000..208757e7 --- /dev/null +++ b/map/poi_geodownload/libmdb/mem.c @@ -0,0 +1,50 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" +#include <locale.h> + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/** + * mdb_init: + * + * Initializes the LibMDB library. This function should be called exactly once + * by calling program and prior to any other function. + * + **/ +void mdb_init(void) +{ + mdb_init_backends(); +} + +/** + * mdb_exit: + * + * Cleans up the LibMDB library. This function should be called exactly once + * by the calling program prior to exiting (or prior to final use of LibMDB + * functions). + * + **/ +void mdb_exit(void) +{ + mdb_remove_backends(); +} diff --git a/map/poi_geodownload/libmdb/money.c b/map/poi_geodownload/libmdb/money.c new file mode 100644 index 00000000..415cdf15 --- /dev/null +++ b/map/poi_geodownload/libmdb/money.c @@ -0,0 +1,140 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 1998-1999 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define MAXPRECISION 20 +/* +** these routines are copied from the freetds project which does something +** very similiar +*/ + +static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier); +static int do_carry(unsigned char *product); +static char *array_to_string(unsigned char *array, int unsigned scale, char *s); +char *mdb_money_to_string(MdbHandle *mdb, int start, char *s); + +/** + * mdb_money_to_string + * @mdb: Handle to open MDB database file + * @start: Offset of the field within the current page + * @s: String that will receieve the value + * + * Returns: the string that has received the value. + */ +char *mdb_money_to_string(MdbHandle *mdb, int start, char *s) +{ + int num_bytes = 8; + int i; + int neg=0; + unsigned char multiplier[MAXPRECISION], temp[MAXPRECISION]; + unsigned char product[MAXPRECISION]; + unsigned char money[num_bytes]; + + memset(multiplier,0,MAXPRECISION); + memset(product,0,MAXPRECISION); + multiplier[0]=1; + memcpy(money, mdb->pg_buf + start, num_bytes); + + /* Perform two's complement for negative numbers */ + if (money[7] & 0x80) { + neg = 1; + for (i=0;i<num_bytes;i++) { + money[i] = ~money[i]; + } + for (i=0; i<num_bytes; i++) { + money[i] ++; + if (money[i]!=0) break; + } + } + + for (i=0;i<num_bytes;i++) { + /* product += multiplier * current byte */ + multiply_byte(product, money[i], multiplier); + + /* multiplier = multiplier * 256 */ + memcpy(temp, multiplier, MAXPRECISION); + memset(multiplier,0,MAXPRECISION); + multiply_byte(multiplier, 256, temp); + } + if (neg) { + s[0]='-'; + array_to_string(product, 4, &s[1]); + } else { + array_to_string(product, 4, s); + } + return s; +} +static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier) +{ + unsigned char number[3]; + unsigned int i, j; + + number[0]=num%10; + number[1]=(num/10)%10; + number[2]=(num/100)%10; + + for (i=0;i<MAXPRECISION;i++) { + if (multiplier[i] == 0) continue; + for (j=0;j<3;j++) { + if (number[j] == 0) continue; + product[i+j] += multiplier[i]*number[j]; + } + do_carry(product); + } + return 0; +} +static int do_carry(unsigned char *product) +{ + unsigned int j; + + for (j=0;j<MAXPRECISION-1;j++) { + if (product[j]>9) { + product[j+1]+=product[j]/10; + product[j]=product[j]%10; + } + } + if (product[j]>9) { + product[j]=product[j]%10; + } + return 0; +} +static char *array_to_string(unsigned char *array, unsigned int scale, char *s) +{ + unsigned int top, i, j=0; + + for (top=MAXPRECISION;(top>0) && (top-1>scale) && !array[top-1];top--); + + if (top == 0) { + s[j++] = '0'; + } else { + for (i=top; i>0; i--) { + if (j == top-scale) s[j++]='.'; + s[j++]=array[i-1]+'0'; + } + } + s[j]='\0'; + + return s; +} diff --git a/map/poi_geodownload/libmdb/options.c b/map/poi_geodownload/libmdb/options.c new file mode 100644 index 00000000..62f2a01f --- /dev/null +++ b/map/poi_geodownload/libmdb/options.c @@ -0,0 +1,86 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2004 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> + +#include <mdbtools.h> + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +#define DEBUG 1 + +static unsigned long opts; +static int optset; + +static void load_options(void); + +void +mdb_debug(int klass, char *fmt, ...) +{ +#ifdef DEBUG + va_list ap; + + if (!optset) load_options(); + if (klass & opts) { + va_start(ap, fmt); + vfprintf (stdout,fmt, ap); + va_end(ap); + fprintf(stdout,"\n"); + } +#endif +} + +static void +load_options(void) +{ + char *opt; + char *s; + + if (!optset && (s=getenv("MDBOPTS"))) { + opt = strtok(s, ":"); + do { + if (!strcmp(opt, "use_index")) opts |= MDB_USE_INDEX; + if (!strcmp(opt, "debug_like")) opts |= MDB_DEBUG_LIKE; + if (!strcmp(opt, "debug_write")) opts |= MDB_DEBUG_WRITE; + if (!strcmp(opt, "debug_usage")) opts |= MDB_DEBUG_USAGE; + if (!strcmp(opt, "debug_ole")) opts |= MDB_DEBUG_OLE; + if (!strcmp(opt, "debug_row")) opts |= MDB_DEBUG_ROW; + if (!strcmp(opt, "debug_all")) { + opts |= MDB_DEBUG_LIKE; + opts |= MDB_DEBUG_WRITE; + opts |= MDB_DEBUG_USAGE; + opts |= MDB_DEBUG_OLE; + opts |= MDB_DEBUG_ROW; + } + opt = strtok(NULL,":"); + } while (opt); + } + optset = 1; +} +int +mdb_get_option(unsigned long optnum) +{ + if (!optset) load_options(); + return ((opts & optnum) > 0); +} diff --git a/map/poi_geodownload/libmdb/props.c b/map/poi_geodownload/libmdb/props.c new file mode 100644 index 00000000..aaf7e63f --- /dev/null +++ b/map/poi_geodownload/libmdb/props.c @@ -0,0 +1,127 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +GPtrArray * +mdb_read_props_list(gchar *kkd, int len) +{ + guint32 record_len; + int pos = 0; + guchar *name; + GPtrArray *names = NULL; + int i = 0; + + names = g_ptr_array_new(); +#ifdef MDB_DEBUG + buffer_dump((const unsigned char*) kkd, 0, len - 1); +#endif + pos = 0; + while (pos < len) { + record_len = mdb_get_int16((unsigned char*)kkd, pos); + pos += 2; +#ifdef MDB_DEBUG + printf("%02d ",i++); + buffer_dump((const unsigned char*)kkd, pos - 2, pos + record_len - 1); +#endif + name = g_malloc(record_len + 1); + strncpy((char*)name, (char*)&kkd[pos], record_len); + name[record_len] = '\0'; + pos += record_len; + g_ptr_array_add(names, name); +#ifdef MDB_DEBUG + printf("new len = %d\n", names->len); +#endif + } + return names; +} +void +mdb_free_props(MdbProperties *props) +{ + if (!props) return; + + if (props->name) g_free(props->name); + g_free(props); +} +static MdbProperties * +mdb_alloc_props(void) +{ + MdbProperties *props; + + props = g_malloc0(sizeof(MdbProperties)); + + return props; +} +MdbProperties * +mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len) +{ + guint32 record_len, name_len; + int pos = 0; + int elem, dtype, dsize; + gchar *name, *value; + MdbProperties *props; + int i = 0; + +#ifdef MDB_DEBUG + buffer_dump((const unsigned char*)kkd, 0, len - 1); +#endif + pos = 0; + + /* skip the name record */ + record_len = mdb_get_int16((unsigned char *)kkd, pos); + pos += 4; + name_len = mdb_get_int16((unsigned char *)kkd, pos); + pos += 2; + props = mdb_alloc_props(); + if (name_len) { + props->name = g_malloc(name_len + 1); + strncpy(props->name, (char*) &kkd[pos], name_len); + props->name[name_len]='\0'; + } + pos += name_len; + + props->hash = g_hash_table_new(g_str_hash, g_str_equal); + + while (pos < len) { + record_len = mdb_get_int16((unsigned char *)kkd, pos); + elem = mdb_get_int16((unsigned char *)kkd, pos + 4); + dtype = kkd[pos + 3]; + dsize = mdb_get_int16((unsigned char *)kkd, pos + 6); + value = g_malloc(dsize + 1); + strncpy(value, (char*) &kkd[pos + 8], dsize); + value[dsize] = '\0'; + name = g_ptr_array_index(names,elem); +#ifdef MDB_DEBUG + printf("%02d ",i++); + buffer_dump((const unsigned char *)kkd, pos, pos + record_len - 1); + printf("elem %d dsize %d dtype %d\n", elem, dsize, dtype); +#endif + if (dtype == MDB_MEMO) dtype = MDB_TEXT; + if (dtype == MDB_BOOL) { + g_hash_table_insert(props->hash, g_strdup(name), g_strdup(kkd[pos + 8] ? "yes" : "no")); + } else { + g_hash_table_insert(props->hash, g_strdup(name), g_strdup(mdb_col_to_string(mdb, (unsigned char *)kkd, pos + 8, dtype, dsize))); + } + g_free(value); + pos += record_len; + } + return props; + +} diff --git a/map/poi_geodownload/libmdb/sargs.c b/map/poi_geodownload/libmdb/sargs.c new file mode 100644 index 00000000..58d02d43 --- /dev/null +++ b/map/poi_geodownload/libmdb/sargs.c @@ -0,0 +1,273 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * code for handling searchable arguments (sargs) used primary by the sql + * engine to support where clause handling. The sargs are configured in + * a tree with AND/OR operators connecting the child nodes. NOT operations + * have only one child on the left side. Logical operators (=,<,>,etc..) + * have no children. + * + * datatype support is a bit weak at this point. To add more types create + * a mdb_test_[type]() function and invoke it from mdb_test_sarg() + */ +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +void +mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data) +{ + if (func(node, data)) + return; + if (node->left) mdb_sql_walk_tree(node->left, func, data); + if (node->right) mdb_sql_walk_tree(node->right, func, data); +} +int +mdb_test_string(MdbSargNode *node, char *s) +{ +int rc; + + if (node->op == MDB_LIKE) { + return mdb_like_cmp(s,node->value.s); + } + rc = strncmp(node->value.s, s, 255); + switch (node->op) { + case MDB_EQUAL: + if (rc==0) return 1; + break; + case MDB_GT: + if (rc<0) return 1; + break; + case MDB_LT: + if (rc>0) return 1; + break; + case MDB_GTEQ: + if (rc<=0) return 1; + break; + case MDB_LTEQ: + if (rc>=0) return 1; + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_string() for operator %d\n",node->op); + break; + } + return 0; +} +int mdb_test_int(MdbSargNode *node, gint32 i) +{ + switch (node->op) { + case MDB_EQUAL: + printf("comparing %x and %x %d\n", i, node->value.i, node->value.i == i); + if (node->value.i == i) return 1; + break; + case MDB_GT: + if (node->value.i < i) return 1; + break; + case MDB_LT: + if (node->value.i > i) return 1; + break; + case MDB_GTEQ: + if (node->value.i <= i) return 1; + break; + case MDB_LTEQ: + if (node->value.i >= i) return 1; + break; + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown operator. Add code to mdb_test_int() for operator %d\n",node->op); + break; + } + return 0; +} +#if 0 +#endif +int +mdb_find_indexable_sargs(MdbSargNode *node, gpointer data) +{ + MdbSarg sarg; + + if (node->op == MDB_OR || node->op == MDB_NOT) return 1; + + /* + * right now all we do is look for sargs that are anded together from + * the root. Later we may put together OR ops into a range, and then + * range scan the leaf pages. That is col1 = 2 or col1 = 4 becomes + * col1 >= 2 and col1 <= 4 for the purpose of index scans, and then + * extra rows are thrown out when the row is tested against the main + * sarg tree. range scans are generally only a bit better than table + * scanning anyway. + * + * also, later we should support the NOT operator, but it's generally + * a pretty worthless test for indexes, ie NOT col1 = 3, we are + * probably better off table scanning. + */ + if (mdb_is_relational_op(node->op) && node->col) { + //printf("op = %d value = %s\n", node->op, node->value.s); + sarg.op = node->op; + sarg.value = node->value; + mdb_add_sarg(node->col, &sarg); + } + return 0; +} +int +mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field) +{ +char tmpbuf[256]; +int lastchar; + + if (node->op == MDB_ISNULL) { + if (field->is_null) return 0; + else return 1; + } else if (node->op == MDB_NOTNULL) { + if (field->is_null) return 1; + else return 0; + } + switch (col->col_type) { + case MDB_BOOL: + return mdb_test_int(node, !field->is_null); + break; + case MDB_BYTE: + return mdb_test_int(node, (gint32)((char *)field->value)[0]); + break; + case MDB_INT: + return mdb_test_int(node, (gint32)mdb_get_int16(field->value, 0)); + break; + case MDB_LONGINT: + return mdb_test_int(node, (gint32)mdb_get_int32(field->value, 0)); + break; + case MDB_TEXT: + if (IS_JET4(mdb)) { + mdb_unicode2ascii(mdb, field->value, 0, field->siz, tmpbuf); + } else { + strncpy(tmpbuf, field->value, 255); + lastchar = field->siz > 255 ? 255 : field->siz; + tmpbuf[lastchar]='\0'; + } + return mdb_test_string(node, tmpbuf); + default: + fprintf(stderr, "Calling mdb_test_sarg on unknown type. Add code to mdb_test_sarg() for type %d\n",col->col_type); + break; + } + return 1; +} +static int +mdb_find_field(int col_num, MdbField *fields, int num_fields) +{ + int i; + + for (i=0;i<num_fields;i++) { + if (fields[i].colnum == col_num) return i; + } + return -1; +} +static int +mdb_test_sarg_node(MdbHandle *mdb, MdbSargNode *node, MdbField *fields, int num_fields) +{ + int elem; + MdbColumn *col; + int rc; + + if (mdb_is_relational_op(node->op)) { + col = node->col; + /* for const = const expressions */ + if (!col) { + return (node->value.i); + } + elem = mdb_find_field(col->col_num, fields, num_fields); + if (!mdb_test_sarg(mdb, col, node, &fields[elem])) + return 0; + } else { /* logical op */ + switch (node->op) { + case MDB_NOT: + rc = mdb_test_sarg_node(mdb, node->left, fields, num_fields); + return !rc; + break; + case MDB_AND: + if (!mdb_test_sarg_node(mdb, node->left, fields, num_fields)) + return 0; + return mdb_test_sarg_node(mdb, node->right, fields, num_fields); + break; + case MDB_OR: + if (mdb_test_sarg_node(mdb, node->left, fields, num_fields)) + return 1; + return mdb_test_sarg_node(mdb, node->right, fields, num_fields); + break; + } + } + return 1; +} +int +mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields) +{ + MdbSargNode *node; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + + node = table->sarg_tree; + + /* there may not be a sarg tree */ + if (!node) return 1; + + return mdb_test_sarg_node(mdb, node, fields, num_fields); +} +#if 0 +int mdb_test_sargs(MdbHandle *mdb, MdbColumn *col, int offset, int len) +{ +MdbSarg *sarg; +int i; + + for (i=0;i<col->num_sargs;i++) { + sarg = g_ptr_array_index (col->sargs, i); + if (!mdb_test_sarg(mdb, col, sarg, offset, len)) { + /* sarg didn't match, no sense going on */ + return 0; + } + } + + return 1; +} +#endif +int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg) +{ +MdbSarg *sarg; + if (!col->sargs) { + col->sargs = g_ptr_array_new(); + } + sarg = g_memdup(in_sarg,sizeof(MdbSarg)); + g_ptr_array_add(col->sargs, sarg); + col->num_sargs++; + + return 1; +} +int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg) +{ + MdbColumn *col; + unsigned int i; + + for (i=0;i<table->num_cols;i++) { + col = g_ptr_array_index (table->columns, i); + if (!strcasecmp(col->name,colname)) { + return mdb_add_sarg(col, in_sarg); + } + } + /* else didn't find the column return 0! */ + return 0; +} diff --git a/map/poi_geodownload/libmdb/stats.c b/map/poi_geodownload/libmdb/stats.c new file mode 100644 index 00000000..1abf2857 --- /dev/null +++ b/map/poi_geodownload/libmdb/stats.c @@ -0,0 +1,74 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/** + * mdb_stats_on: + * @mdb: Handle to the (open) MDB file to collect stats on. + * + * Begins collection of statistics on an MDBHandle. + * + * Statistics in LibMDB will track the number of reads from the MDB file. The + * collection of statistics is started and stopped with the mdb_stats_on and + * mdb_stats_off functions. Collected statistics are accessed by reading the + * MdbStatistics structure or calling mdb_dump_stats. + * + */ +void +mdb_stats_on(MdbHandle *mdb) +{ + if (!mdb->stats) + mdb->stats = g_malloc0(sizeof(MdbStatistics)); + + mdb->stats->collect = TRUE; +} +/** + * mdb_stats_off: + * @mdb: pointer to handle of MDB file with active stats collection. + * + * Turns off statistics collection. + * + * If mdb_stats_off is not called, statistics will be turned off when handle + * is freed using mdb_close. + **/ +void +mdb_stats_off(MdbHandle *mdb) +{ + if (!mdb->stats) return; + + mdb->stats->collect = FALSE; +} +/** + * mdb_dump_stats: + * @mdb: pointer to handle of MDB file with active stats collection. + * + * Dumps current statistics to stdout. + **/ +void +mdb_dump_stats(MdbHandle *mdb) +{ + if (!mdb->stats) return; + + fprintf(stdout, "Physical Page Reads: %lu\n", mdb->stats->pg_reads); +} diff --git a/map/poi_geodownload/libmdb/table.c b/map/poi_geodownload/libmdb/table.c new file mode 100644 index 00000000..c1960075 --- /dev/null +++ b/map/poi_geodownload/libmdb/table.c @@ -0,0 +1,370 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b) +{ + if ((*a)->col_num > (*b)->col_num) + return 1; + else if ((*a)->col_num < (*b)->col_num) + return -1; + else + return 0; +} + +#if 0 +static unsigned char mdb_col_needs_size(int col_type) +{ + if (col_type == MDB_TEXT) { + return TRUE; + } else { + return FALSE; + } +} +#endif + +MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry) +{ + MdbTableDef *table; + + table = (MdbTableDef *) g_malloc0(sizeof(MdbTableDef)); + table->entry=entry; + strcpy(table->name, entry->object_name); + + return table; +} +void mdb_free_tabledef(MdbTableDef *table) +{ + if (!table) return; + if (table->is_temp_table) { + unsigned int i; + for (i=0; i<table->temp_table_pages->len; i++) + g_free(g_ptr_array_index(table->temp_table_pages,i)); + g_ptr_array_free(table->temp_table_pages, TRUE); + } + mdb_free_columns(table->columns); + mdb_free_indices(table->indices); + g_free(table->usage_map); + g_free(table->free_usage_map); + g_free(table); +} +MdbTableDef *mdb_read_table(MdbCatalogEntry *entry) +{ + MdbTableDef *table; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + int len, row_start, pg_row; + unsigned char *buf; + + table = mdb_alloc_tabledef(entry); + + mdb_read_pg(mdb, entry->table_pg); + if (mdb->pg_buf[0] != 0x02) return NULL; /* not a valid table def page */ + + len = mdb_pg_get_int16(mdb,8); + + table->num_rows = mdb_pg_get_int32(mdb, fmt->tab_num_rows_offset); + table->num_var_cols = mdb_pg_get_int16(mdb, fmt->tab_num_cols_offset-2); + table->num_cols = mdb_pg_get_int16(mdb, fmt->tab_num_cols_offset); + table->num_idxs = mdb_pg_get_int32(mdb, fmt->tab_num_idxs_offset); + table->num_real_idxs = mdb_pg_get_int32(mdb, fmt->tab_num_ridxs_offset); + /* grab a copy of the usage map */ + pg_row = mdb_pg_get_int32(mdb, fmt->tab_usage_map_offset); + mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz)); + table->usage_map = g_memdup(buf + row_start, table->map_sz); + if (mdb_get_option(MDB_DEBUG_USAGE)) + buffer_dump(buf, row_start, row_start+table->map_sz-1); + mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d", + pg_row >> 8, pg_row & 0xff, row_start, table->map_sz); + + /* grab a copy of the free space page map */ + pg_row = mdb_pg_get_int32(mdb, fmt->tab_free_map_offset); + mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz)); + table->free_usage_map = g_memdup(buf + row_start, table->freemap_sz); + mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n", + pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz); + + table->first_data_pg = mdb_pg_get_int16(mdb, fmt->tab_first_dpg_offset); + + return table; +} +MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type) +{ + unsigned int i; + MdbCatalogEntry *entry; + + mdb_read_catalog(mdb, obj_type); + + for (i=0; i<mdb->num_catalog; i++) { + entry = g_ptr_array_index(mdb->catalog, i); + if (!strcasecmp(entry->object_name, table_name)) + return mdb_read_table(entry); + } + + return NULL; +} + +/* +** read the next page if offset is > pg_size +** return true if page was read +*/ +int +read_pg_if(MdbHandle *mdb, int *cur_pos, int offset) +{ + if (*cur_pos + offset >= mdb->fmt->pg_size) { + mdb_read_pg(mdb, mdb_pg_get_int32(mdb,4)); + *cur_pos = 8 - (mdb->fmt->pg_size - (*cur_pos)); + return 1; + } + return 0; +} +guint32 +read_pg_if_32(MdbHandle *mdb, int *cur_pos) +{ + unsigned char c[4]; + int i, rc = 0; + + for (i=0;i<4;i++) { + rc += read_pg_if(mdb, cur_pos, i); + c[i] = mdb->pg_buf[(*cur_pos) + i]; + } + return mdb_get_int32(c, 0); +} +guint16 +read_pg_if_16(MdbHandle *mdb, int *cur_pos) +{ + unsigned char low_byte, high_byte; + int rc = 0; + + rc += read_pg_if(mdb, cur_pos, 0); + low_byte = mdb->pg_buf[*cur_pos]; + rc += read_pg_if(mdb, cur_pos, 1); + high_byte = mdb->pg_buf[(*cur_pos) + 1]; + + return (high_byte * 256 + low_byte); +} +guint16 +read_pg_if_n(MdbHandle *mdb, unsigned char *buf, int *cur_pos, int len) +{ + if (*cur_pos + len < mdb->fmt->pg_size) { + memcpy(buf, &mdb->pg_buf[*cur_pos], len); + return 0; + } else { + int half = mdb->fmt->pg_size - *cur_pos; + memcpy(buf, &mdb->pg_buf[*cur_pos], half); + mdb_read_pg(mdb, mdb_pg_get_int32(mdb,4)); + memcpy(buf + half, &mdb->pg_buf[8], len - half); + *cur_pos = 8 - half; + return 1; + } +} + +void mdb_append_column(GPtrArray *columns, MdbColumn *in_col) +{ + g_ptr_array_add(columns, g_memdup(in_col,sizeof(MdbColumn))); +} +void mdb_free_columns(GPtrArray *columns) +{ + unsigned int i; + + if (!columns) return; + for (i=0; i<columns->len; i++) + g_free (g_ptr_array_index(columns, i)); + g_ptr_array_free(columns, TRUE); +} +GPtrArray *mdb_read_columns(MdbTableDef *table) +{ + MdbHandle *mdb = table->entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + MdbColumn *pcol; + unsigned char *col; + unsigned int i; + int cur_pos, name_sz; + + table->columns = g_ptr_array_new(); + + col = (unsigned char *) g_malloc(fmt->tab_col_entry_size); + + cur_pos = fmt->tab_cols_start_offset + + (table->num_real_idxs * fmt->tab_ridx_entry_size); + + /* new code based on patch submitted by Tim Nelson 2000.09.27 */ + + /* + ** column attributes + */ + for (i=0;i<table->num_cols;i++) { +#ifdef MDB_DEBUG + /* printf("column %d\n", i); + buffer_dump(mdb->pg_buf, cur_pos ,cur_pos + 18); */ +#endif + read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size); + cur_pos += fmt->tab_col_entry_size; + pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn)); + + pcol->col_type = col[0]; + + // col_num_offset == 1 or 5 + pcol->col_num = col[fmt->col_num_offset]; + + //fprintf(stdout,"----- column %d -----\n",pcol->col_num); + // col_var == 3 or 7 + pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var); + //fprintf(stdout,"var column pos %d\n",pcol->var_col_num); + + // col_var == 5 or 9 + pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset); + //fprintf(stdout,"row column num %d\n",pcol->row_col_num); + + /* FIXME: can this be right in Jet3 and Jet4? */ + if (pcol->col_type == MDB_NUMERIC) { + pcol->col_prec = col[11]; + pcol->col_scale = col[12]; + } + + // col_fixed_offset == 13 or 15 + pcol->is_fixed = col[fmt->col_fixed_offset] & 0x01 ? 1 : 0; + + // col_fixed_offset == 13 or 15 + pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed); + //fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset); + //fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable"); + + if (pcol->col_type != MDB_BOOL) { + // col_size_offset == 16 or 23 + pcol->col_size = mdb_get_int16(col, fmt->col_size_offset); + } else { + pcol->col_size=0; + } + + g_ptr_array_add(table->columns, pcol); + } + + g_free (col); + + /* + ** column names - ordered the same as the column attributes table + */ + for (i=0;i<table->num_cols;i++) { + pcol = g_ptr_array_index(table->columns, i); + + if (IS_JET4(mdb)) { + unsigned char *tmp_buf; + name_sz = read_pg_if_16(mdb, &cur_pos); + cur_pos += 2; + tmp_buf = (unsigned char *) g_malloc(name_sz); + read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz); + mdb_unicode2ascii(mdb, tmp_buf, 0, name_sz, pcol->name); + g_free(tmp_buf); + cur_pos += name_sz; + } else if (IS_JET3(mdb)) { + read_pg_if(mdb, &cur_pos, 0); + name_sz = mdb->pg_buf[cur_pos]; + cur_pos++; + read_pg_if_n(mdb, (unsigned char*) pcol->name, &cur_pos, name_sz); + pcol->name[name_sz]='\0'; + cur_pos += name_sz; + } else { + fprintf(stderr,"Unknown MDB version\n"); + } + } + + /* Sort the columns by col_num */ + g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer); + + table->index_start = cur_pos; + return table->columns; +} + +void mdb_table_dump(MdbCatalogEntry *entry) +{ +MdbTableDef *table; +MdbColumn *col; +int coln; +MdbIndex *idx; +MdbHandle *mdb = entry->mdb; +unsigned int i, bitn; +guint32 pgnum; + + table = mdb_read_table(entry); + fprintf(stdout,"definition page = %lu\n",entry->table_pg); + fprintf(stdout,"number of datarows = %d\n",table->num_rows); + fprintf(stdout,"number of columns = %d\n",table->num_cols); + fprintf(stdout,"number of indices = %d\n",table->num_real_idxs); + + mdb_read_columns(table); + mdb_read_indices(table); + + for (i=0;i<table->num_cols;i++) { + col = g_ptr_array_index(table->columns,i); + + fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n", + i, col->name, + mdb_get_coltype_string(mdb->default_backend, col->col_type), + col->col_size); + } + + for (i=0;i<table->num_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + mdb_index_dump(table, idx); + } + if (table->usage_map) { + printf("pages reserved by this object\n"); + printf("usage map pg %" G_GUINT32_FORMAT "\n", + table->map_base_pg); + printf("free map pg %" G_GUINT32_FORMAT "\n", + table->freemap_base_pg); + pgnum = mdb_get_int32(table->usage_map,1); + /* the first 5 bytes of the usage map mean something */ + coln = 0; + for (i=5;i<table->map_sz;i++) { + for (bitn=0;bitn<8;bitn++) { + if (table->usage_map[i] & 1 << bitn) { + coln++; + printf("%6" G_GUINT32_FORMAT, pgnum); + if (coln==10) { + printf("\n"); + coln = 0; + } else { + printf(" "); + } + } + pgnum++; + } + } + printf("\n"); + } +} + +int mdb_is_user_table(MdbCatalogEntry *entry) +{ + return ((entry->object_type == MDB_TABLE) + && !(entry->flags & 0x80000002)) ? 1 : 0; +} +int mdb_is_system_table(MdbCatalogEntry *entry) +{ + return ((entry->object_type == MDB_TABLE) + && (entry->flags & 0x80000002)) ? 1 : 0; +} diff --git a/map/poi_geodownload/libmdb/worktable.c b/map/poi_geodownload/libmdb/worktable.c new file mode 100644 index 00000000..6f893dcf --- /dev/null +++ b/map/poi_geodownload/libmdb/worktable.c @@ -0,0 +1,99 @@ +/* MDB Tools - A library for reading MS Access database files + * Copyright (C) 2004 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + +/* + * Temp table routines. These are currently used to generate mock results for + * commands like "list tables" and "describe table" + */ + +void +mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed) +{ + memset(tcol,0,sizeof(MdbColumn)); + strcpy(tcol->name, col_name); + tcol->col_type = col_type; + if ((col_type == MDB_TEXT) || (col_type == MDB_MEMO)) { + tcol->col_size = col_size; + } else { + tcol->col_size = mdb_col_fixed_size(tcol); + } + tcol->is_fixed = is_fixed; +} +void +mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int colnum) +{ + field->value = value; + field->siz = siz; + field->is_fixed = is_fixed; + field->is_null = is_null; + field->start = start; + field->colnum = colnum; +} +MdbTableDef * +mdb_create_temp_table(MdbHandle *mdb, char *name) +{ + MdbCatalogEntry *entry; + MdbTableDef *table; + + /* dummy up a catalog entry */ + entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry)); + entry->mdb = mdb; + entry->object_type = MDB_TABLE; + entry->table_pg = 0; + strcpy(entry->object_name, name); + + table = mdb_alloc_tabledef(entry); + table->columns = g_ptr_array_new(); + table->is_temp_table = 1; + table->temp_table_pages = g_ptr_array_new(); + + return table; +} +void +mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col) +{ + col->col_num = table->num_cols; + if (!col->is_fixed) + col->var_col_num = table->num_var_cols++; + g_ptr_array_add(table->columns, g_memdup(col, sizeof(MdbColumn))); + table->num_cols++; +} +/* + * Should be called after setting up all temp table columns + */ +void mdb_temp_columns_end(MdbTableDef *table) +{ + MdbColumn *col; + unsigned int i; + unsigned int start = 0; + + for (i=0; i<table->num_cols; i++) { + col = g_ptr_array_index(table->columns, i); + if (col->is_fixed) { + col->fixed_offset = start; + start += col->col_size; + } + } +} diff --git a/map/poi_geodownload/libmdb/write.c b/map/poi_geodownload/libmdb/write.c new file mode 100644 index 00000000..a4b82ed6 --- /dev/null +++ b/map/poi_geodownload/libmdb/write.c @@ -0,0 +1,883 @@ +/* MDB Tools - A library for reading MS Access database file + * Copyright (C) 2000 Brian Bruns + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "mdbtools.h" +#include "time.h" +#include "math.h" + +#ifdef DMALLOC +#include "dmalloc.h" +#endif + + +//static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg); +static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields); + +static void +_mdb_put_int16(unsigned char *buf, guint32 offset, guint32 value) +{ + buf[offset] = value % 256; + value /= 256; + buf[offset+1] = value % 256; +} +static void +_mdb_put_int32(unsigned char *buf, guint32 offset, guint32 value) +{ + buf[offset] = value % 256; + value /= 256; + buf[offset+1] = value % 256; + value /= 256; + buf[offset+2] = value % 256; + value /= 256; + buf[offset+3] = value % 256; +} +static ssize_t +mdb_write_pg(MdbHandle *mdb, unsigned long pg) +{ + ssize_t len; + struct stat status; + off_t offset = pg * mdb->fmt->pg_size; + + fstat(mdb->f->fd, &status); + /* is page beyond current size + 1 ? */ + if (status.st_size < offset + mdb->fmt->pg_size) { + fprintf(stderr,"offset %lu is beyond EOF\n",offset); + return 0; + } + lseek(mdb->f->fd, offset, SEEK_SET); + len = write(mdb->f->fd,mdb->pg_buf,mdb->fmt->pg_size); + if (len==-1) { + perror("write"); + return 0; + } else if (len<mdb->fmt->pg_size) { + /* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */ + return 0; + } + mdb->cur_pos = 0; + return len; +} + +static int +mdb_is_col_indexed(MdbTableDef *table, int colnum) +{ + unsigned int i, j; + MdbIndex *idx; + + for (i=0;i<table->num_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + for (j=0;j<idx->num_keys;j++) { + if (idx->key_col_num[j]==colnum) return 1; + } + } + return 0; +} +static int +mdb_crack_row4(MdbTableDef *table, int row_start, int row_end, MdbField *fields) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbColumn *col; + unsigned char *pg_buf = mdb->pg_buf; + unsigned int i; + unsigned int row_var_cols=0, row_fixed_cols, row_cols; + unsigned int fixed_cols_found; + unsigned int col_start; + unsigned char *nullmask; + unsigned int bitmask_sz; + unsigned int byte_num, bit_num; + unsigned int *var_col_offsets = NULL; + + if (mdb_get_option(MDB_DEBUG_ROW)) { + buffer_dump(pg_buf, row_start, row_end); + } + + row_cols = mdb_pg_get_int16(mdb, row_start); + + bitmask_sz = (row_cols + 7) / 8; + nullmask = &pg_buf[row_end - bitmask_sz + 1]; + + /* read table of variable column locations */ + if (table->num_var_cols > 0) { + row_var_cols = mdb_pg_get_int16(mdb, row_end - bitmask_sz - 1); + var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int)); + for (i=0; i<row_var_cols+1; i++) { + var_col_offsets[i] = mdb_pg_get_int16(mdb, + row_end - bitmask_sz - 3 - (i*2)); + } + } + fixed_cols_found = 0; + row_fixed_cols = row_cols - row_var_cols; + + /* read information into fields[] */ + for (i=0;i<table->num_cols;i++) { + col = g_ptr_array_index(table->columns,i); + fields[i].colnum = i; + fields[i].is_fixed = (mdb_is_fixed_col(col)) ? 1 : 0; + byte_num = col->col_num / 8; + bit_num = col->col_num % 8; + /* logic on nulls is reverse, 1 is not null, 0 is null */ + fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1; + + if ((fields[i].is_fixed) + && (fixed_cols_found < row_fixed_cols)) { + col_start = col->fixed_offset + 2; + fields[i].start = row_start + col_start; + fields[i].value = &pg_buf[row_start + col_start]; + fields[i].siz = col->col_size; + fixed_cols_found++; + /* Use col->var_col_num because a deleted column is still + * present in the variable column offsets table for the row */ + } else if ((!fields[i].is_fixed) + && (col->var_col_num < row_var_cols)) { + col_start = var_col_offsets[col->var_col_num]; + fields[i].start = row_start + col_start; + fields[i].value = &pg_buf[row_start + col_start]; + fields[i].siz = var_col_offsets[(col->var_col_num)+1] - + col_start; + } else { + fields[i].start = 0; + fields[i].value = NULL; + fields[i].siz = 0; + fields[i].is_null = 1; + } + } + g_free(var_col_offsets); + + return row_cols; +} +static int +mdb_crack_row3(MdbTableDef *table, int row_start, int row_end, MdbField *fields) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbColumn *col; + unsigned char *pg_buf = mdb->pg_buf; + unsigned int i; + unsigned int row_var_cols = 0, row_fixed_cols, row_cols; + unsigned int fixed_cols_found, var_cols_found; + unsigned int col_start; + unsigned char *nullmask; + unsigned int bitmask_sz; + unsigned int byte_num, bit_num; + unsigned int *var_col_offsets = NULL; + unsigned int num_jumps = 0, jumps_used = 0; + unsigned int col_ptr, row_len; + + if (mdb_get_option(MDB_DEBUG_ROW)) { + buffer_dump(pg_buf, row_start, row_end); + } + + row_cols = pg_buf[row_start]; + + bitmask_sz = (row_cols + 7) / 8; + nullmask = &pg_buf[row_end - bitmask_sz + 1]; + + /* read table of variable column locations */ + if (table->num_var_cols > 0) { + row_var_cols = pg_buf[row_end - bitmask_sz]; + row_len = row_end - row_start + 1; + num_jumps = (row_len - 1) / 256; + col_ptr = row_end - bitmask_sz - num_jumps - 1; + /* If last jump is a dummy value, ignore it */ + if ((col_ptr-row_start-row_var_cols)/256 < num_jumps) + num_jumps--; + + var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int)); + jumps_used = 0; + for (i=0; i<row_var_cols+1; i++) { + if ((jumps_used < num_jumps) + && (i == pg_buf[row_end-bitmask_sz-jumps_used-1])) { + jumps_used++; + } + var_col_offsets[i] = pg_buf[col_ptr-i]+(jumps_used*256); + } + } + fixed_cols_found = 0; + var_cols_found = 0; + row_fixed_cols = row_cols - row_var_cols; + + if (mdb_get_option(MDB_DEBUG_ROW)) { + fprintf(stdout,"bitmask_sz %d num_jumps %d\n",bitmask_sz, num_jumps); + fprintf(stdout,"row_var_cols %d\n", row_var_cols); + fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols); + } + + /* read information into fields[] */ + for (i=0;i<table->num_cols;i++) { + col = g_ptr_array_index (table->columns, i); + fields[i].colnum = i; + fields[i].is_fixed = (mdb_is_fixed_col(col)) ? 1 : 0; + byte_num = col->col_num / 8; + bit_num = col->col_num % 8; + /* logic on nulls is reverse, 1 is not null, 0 is null */ + fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1; + + if ((fields[i].is_fixed) + && (fixed_cols_found < row_fixed_cols)) { + col_start = col->fixed_offset + 1; + fields[i].start = row_start + col_start; + fields[i].value = &pg_buf[row_start + col_start]; + fields[i].siz = col->col_size; + fixed_cols_found++; + } else if ((!fields[i].is_fixed) + && (var_cols_found < row_var_cols)) { + col_start = var_col_offsets[var_cols_found]; + fields[i].start = row_start + col_start; + fields[i].value = &pg_buf[row_start + col_start]; + fields[i].siz = var_col_offsets[var_cols_found+1] - + col_start; + var_cols_found++; + } else { + fields[i].start = 0; + fields[i].value = NULL; + fields[i].siz = 0; + fields[i].is_null = 1; + } + } + g_free(var_col_offsets); + + return row_cols; +} +/** + * mdb_crack_row: + * @table: Table that the row belongs to + * @row_start: offset to start of row on current page + * @row_end: offset to end of row on current page + * @fields: pointer to MdbField array to be popluated by mdb_crack_row + * + * Cracks a row buffer apart into its component fields. + * + * A row buffer is that portion of a data page which contains the values for + * that row. Its beginning and end can be found in the row offset table. + * + * The resulting MdbField array contains pointers into the row for each field + * present. Be aware that by modifying field[]->value, you would be modifying + * the row buffer itself, not a copy. + * + * This routine is mostly used internally by mdb_fetch_row() but may have some + * applicability for advanced application programs. + * + * Return value: number of fields present. + */ +int +mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields) +{ +MdbCatalogEntry *entry = table->entry; +MdbHandle *mdb = entry->mdb; + + if (IS_JET4(mdb)) { + return mdb_crack_row4(table, row_start, row_end, fields); + } else { + return mdb_crack_row3(table, row_start, row_end, fields); + } +} + +static int +mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields) +{ + int pos = 0, bit = 0, byte = 0; + int i; + + /* 'Not null' bitmap */ + for (i=0; i<num_fields; i++) { + /* column is null if bit is clear (0) */ + if (!fields[i].is_null) { + byte |= 1 << bit; + //printf("%d %d %d %d\n", i, bit, 1 << bit, byte); + } + bit++; + if (bit==8) { + buffer[pos++] = byte; + bit = byte = 0; + } + } + /* if we've written any bits to the current byte, flush it */ + if (bit) + buffer[pos++] = byte; + + return pos; +} +/* fields must be ordered with fixed columns first, then vars, subsorted by + * column number */ +static int +mdb_pack_row4(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields) +{ + unsigned int pos = 0; + unsigned int var_cols = 0; + unsigned int i; + + row_buffer[pos++] = num_fields & 0xff; + row_buffer[pos++] = (num_fields >> 8) & 0xff; + + /* Fixed length columns */ + for (i=0;i<num_fields;i++) { + if (fields[i].is_fixed) { + fields[i].offset = pos; + if (!fields[i].is_null) { + memcpy(&row_buffer[pos], fields[i].value, fields[i].siz); + } + pos += fields[i].siz; + } + } + /* For tables without variable-length columns */ + if (table->num_var_cols == 0) { + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; + } + /* Variable length columns */ + for (i=0;i<num_fields;i++) { + if (!fields[i].is_fixed) { + var_cols++; + fields[i].offset = pos; + if (! fields[i].is_null) { + memcpy(&row_buffer[pos], fields[i].value, fields[i].siz); + pos += fields[i].siz; + } + } + } + /* EOD */ + row_buffer[pos] = pos & 0xff; + row_buffer[pos+1] = (pos >> 8) & 0xff; + pos += 2; + + /* Offsets of the variable-length columns */ + for (i=num_fields; i>0; i--) { + if (!fields[i-1].is_fixed) { + row_buffer[pos++] = fields[i-1].offset & 0xff; + row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff; + } + } + /* Number of variable-length columns */ + row_buffer[pos++] = var_cols & 0xff; + row_buffer[pos++] = (var_cols >> 8) & 0xff; + + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; +} + +static int +mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields) +{ + unsigned int pos = 0; + unsigned int var_cols = 0; + unsigned int i, j; + unsigned char *offset_high; + + row_buffer[pos++] = num_fields; + + /* Fixed length columns */ + for (i=0;i<num_fields;i++) { + if (fields[i].is_fixed) { + fields[i].offset = pos; + if (!fields[i].is_null) { + memcpy(&row_buffer[pos], fields[i].value, fields[i].siz); + } + pos += fields[i].siz; + } + } + /* For tables without variable-length columns */ + if (table->num_var_cols == 0) { + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; + } + /* Variable length columns */ + for (i=0;i<num_fields;i++) { + if (!fields[i].is_fixed) { + var_cols++; + fields[i].offset = pos; + if (! fields[i].is_null) { + memcpy(&row_buffer[pos], fields[i].value, fields[i].siz); + pos += fields[i].siz; + } + } + } + + offset_high = (unsigned char *) g_malloc(var_cols+1); + offset_high[0] = (pos << 8) & 0xff; + j = 1; + + /* EOD */ + row_buffer[pos] = pos & 0xff; + pos++; + + /* Variable length column offsets */ + for (i=num_fields; i>0; i--) { + if (!fields[i-1].is_fixed) { + row_buffer[pos++] = fields[i-1].offset & 0xff; + offset_high[j++] = (fields[i-1].offset << 8) & 0xff; + } + } + + /* Dummy jump table entry */ + if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) { + row_buffer[pos++] = 0xff; + } + /* Jump table */ + for (i=0; i<var_cols; i++) { + if (offset_high[i] > offset_high[i+1]) { + row_buffer[pos++] = var_cols-i; + } + } + g_free(offset_high); + + row_buffer[pos++] = var_cols; + + pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields); + return pos; +} +int +mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields) +{ + if (table->is_temp_table) { + unsigned int i; + for (i=0; i<num_fields; i++) { + MdbColumn *c = g_ptr_array_index(table->columns, i); + fields[i].is_null = (fields[i].value) ? 0 : 1; + fields[i].colnum = i; + fields[i].is_fixed = c->is_fixed; + if ((c->col_type != MDB_TEXT) + && (c->col_type != MDB_MEMO)) { + fields[i].siz = c->col_size; + } + } + } + if (IS_JET4(table->entry->mdb)) { + return mdb_pack_row4(table, row_buffer, num_fields, fields); + } else { + return mdb_pack_row3(table, row_buffer, num_fields, fields); + } +} +int +mdb_pg_get_freespace(MdbHandle *mdb) +{ + int rows, free_start, free_end; + int row_count_offset = mdb->fmt->row_count_offset; + + rows = mdb_pg_get_int16(mdb, row_count_offset); + free_start = row_count_offset + 2 + (rows * 2); + free_end = mdb_pg_get_int16(mdb, row_count_offset + (rows * 2)); + mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start); + return (free_end - free_start); +} +static unsigned char * +mdb_new_leaf_pg(MdbCatalogEntry *entry) +{ + MdbHandle *mdb = entry->mdb; + unsigned char *new_pg; + + new_pg = (unsigned char *) g_malloc0(mdb->fmt->pg_size); + + new_pg[0]=0x04; + new_pg[1]=0x01; + _mdb_put_int32(new_pg, 4, entry->table_pg); + + return new_pg; +} +unsigned char * +mdb_new_data_pg(MdbCatalogEntry *entry) +{ + MdbFormatConstants *fmt = entry->mdb->fmt; + unsigned char *new_pg; + + new_pg = (unsigned char *) g_malloc0(fmt->pg_size); + + new_pg[0]=0x01; + new_pg[1]=0x01; + _mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2); + _mdb_put_int32(new_pg, 4, entry->table_pg); + + return new_pg; +} + +#if 0 +static int +mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum) +{ + unsigned int i; + MdbIndex *idx; + + for (i=0;i<table->num_idxs;i++) { + idx = g_ptr_array_index (table->indices, i); + mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type); + if (idx->index_type==1) { + mdb_update_index(table, idx, num_fields, fields, pgnum, rownum); + } + } + return 1; +} + +static int +mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + + table->scan_idx = idx; + table->chain = g_malloc0(sizeof(MdbIndexChain)); + table->mdbidx = mdb_clone_handle(mdb); + mdb_read_pg(table->mdbidx, table->scan_idx->first_pg); + + return 1; +} +#endif + +int +mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum) +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + int idx_xref[16]; + unsigned int i, j; + MdbIndexChain *chain; + MdbField idx_fields[10]; + + for (i = 0; i < idx->num_keys; i++) { + for (j = 0; j < num_fields; j++) { + // key_col_num is 1 based, can't remember why though + if (fields[j].colnum == idx->key_col_num[i]-1) { + idx_xref[i] = j; + idx_fields[i] = fields[j]; + } + } + } + for (i = 0; i < idx->num_keys; i++) { + fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n", + i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum, + fields[idx_xref[i]].siz); + } + for (i = 0; i < num_fields; i++) { + fprintf(stdout, "%d (%d %d)\n", + i, fields[i].colnum, + fields[i].siz); + } + + chain = g_malloc0(sizeof(MdbIndexChain)); + + mdb_index_find_row(mdb, idx, chain, pgnum, rownum); + printf("chain depth = %d\n", chain->cur_depth); + printf("pg = %" G_GUINT32_FORMAT "\n", + chain->pages[chain->cur_depth-1].pg); + //mdb_copy_index_pg(table, idx, &chain->pages[chain->cur_depth-1]); + mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields); + + return 1; +} + +#if 0 +static int +mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields) +{ + int new_row_size; + unsigned char row_buffer[4096]; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + guint32 pgnum; + guint16 rownum; + + if (!mdb->f->writable) { + fprintf(stderr, "File is not open for writing\n"); + return 0; + } + new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields); + if (mdb_get_option(MDB_DEBUG_WRITE)) { + buffer_dump(row_buffer, 0, new_row_size-1); + } + pgnum = mdb_map_find_next_freepage(table, new_row_size); + if (!pgnum) { + fprintf(stderr, "Unable to allocate new page.\n"); + return 0; + } + + rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size); + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + buffer_dump(mdb->pg_buf, 0, 39); + buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1); + } + mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum); + if (!mdb_write_pg(mdb, pgnum)) { + fprintf(stderr, "write failed! exiting...\n"); + exit(1); + } + + mdb_update_indexes(table, num_fields, fields, pgnum, rownum); + + return 1; +} +#endif + +/* + * Assumes caller has verfied space is available on page and adds the new + * row to the current pg_buf. + */ +guint16 +mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size) +{ + unsigned char *new_pg; + int num_rows, i, pos, row_start, row_end, row_size; + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbFormatConstants *fmt = mdb->fmt; + + if (table->is_temp_table) { + GPtrArray *pages = table->temp_table_pages; + if (pages->len == 0) { + new_pg = mdb_new_data_pg(entry); + g_ptr_array_add(pages, new_pg); + } else { + new_pg = g_ptr_array_index(pages, pages->len - 1); + if (mdb_get_int16(new_pg, 2) < new_row_size + 2) { + new_pg = mdb_new_data_pg(entry); + g_ptr_array_add(pages, new_pg); + } + } + + num_rows = mdb_get_int16(new_pg, fmt->row_count_offset); + pos = (num_rows == 0) ? fmt->pg_size : + mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2)); + } else { /* is not a temp table */ + new_pg = mdb_new_data_pg(entry); + + num_rows = mdb_pg_get_int16(mdb, fmt->row_count_offset); + pos = fmt->pg_size; + + /* copy existing rows */ + for (i=0;i<num_rows;i++) { + row_start = mdb_pg_get_int16(mdb, (fmt->row_count_offset + 2) + (i*2)); + row_end = mdb_find_end_of_row(mdb, i); + row_size = row_end - row_start + 1; + pos -= row_size; + memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); + } + } + + /* add our new row */ + pos -= new_row_size; + memcpy(&new_pg[pos], row_buffer, new_row_size); + /* add row to the row offset table */ + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos); + + /* update number rows on this page */ + num_rows++; + _mdb_put_int16(new_pg, fmt->row_count_offset, num_rows); + + /* update the freespace */ + _mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2)); + + /* copy new page over old */ + if (!table->is_temp_table) { + memcpy(mdb->pg_buf, new_pg, fmt->pg_size); + g_free(new_pg); + } + + return num_rows; +} +int +mdb_update_row(MdbTableDef *table) +{ +int row_start, row_end; +unsigned int i; +MdbColumn *col; +MdbCatalogEntry *entry = table->entry; +MdbHandle *mdb = entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; +MdbField fields[256]; +unsigned char row_buffer[4096]; +int old_row_size, new_row_size, delta; +unsigned int num_fields; + + if (!mdb->f->writable) { + fprintf(stderr, "File is not open for writing\n"); + return 0; + } + row_start = mdb_pg_get_int16(mdb, (fmt->row_count_offset + 2) + ((table->cur_row-1)*2)); + row_end = mdb_find_end_of_row(mdb, table->cur_row-1); + old_row_size = row_end - row_start; + + row_start &= 0x0FFF; /* remove flags */ + + mdb_debug(MDB_DEBUG_WRITE,"page %lu row %d start %d end %d", (unsigned long) table->cur_phys_pg, table->cur_row-1, row_start, row_end); + if (mdb_get_option(MDB_DEBUG_LIKE)) + buffer_dump(mdb->pg_buf, row_start, row_end); + + for (i=0;i<table->num_cols;i++) { + col = g_ptr_array_index(table->columns,i); + if (col->bind_ptr && mdb_is_col_indexed(table,i)) { + fprintf(stderr, "Attempting to update column that is part of an index\n"); + return 0; + } + } + num_fields = mdb_crack_row(table, row_start, row_end, fields); + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + for (i=0;i<num_fields;i++) { + printf("col %d %d start %d siz %d\n", i, fields[i].colnum, fields[i].start, fields[i].siz); + } + } + for (i=0;i<table->num_cols;i++) { + col = g_ptr_array_index(table->columns,i); + if (col->bind_ptr) { + printf("yes\n"); + fields[i].value = col->bind_ptr; + fields[i].siz = *(col->len_ptr); + } + } + + new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields); + if (mdb_get_option(MDB_DEBUG_WRITE)) + buffer_dump(row_buffer, 0, new_row_size-1); + delta = new_row_size - old_row_size; + if ((mdb_pg_get_freespace(mdb) - delta) < 0) { + fprintf(stderr, "No space left on this page, update will not occur\n"); + return 0; + } + /* do it! */ + mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size); + return 0; +} +int +mdb_replace_row(MdbTableDef *table, int row, unsigned char *new_row, int new_row_size) +{ +MdbCatalogEntry *entry = table->entry; +MdbHandle *mdb = entry->mdb; +MdbFormatConstants *fmt = mdb->fmt; +unsigned char *new_pg; +guint16 num_rows; +int row_start, row_end, row_size; +int i, pos; + + if (mdb_get_option(MDB_DEBUG_WRITE)) { + buffer_dump(mdb->pg_buf, 0, 39); + buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1); + } + mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg); + new_pg = mdb_new_data_pg(entry); + + num_rows = mdb_pg_get_int16(mdb, fmt->row_count_offset); + _mdb_put_int16(new_pg, fmt->row_count_offset, num_rows); + + pos = mdb->fmt->pg_size; + + /* rows before */ + for (i=0;i<row;i++) { + row_start = mdb_pg_get_int16(mdb, (fmt->row_count_offset + 2) + (i*2)); + row_end = mdb_find_end_of_row(mdb, i); + row_size = row_end - row_start + 1; + pos -= row_size; + memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); + } + + /* our row */ + pos -= new_row_size; + memcpy(&new_pg[pos], new_row, new_row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (row*2), pos); + + /* rows after */ + for (i=row+1;i<num_rows;i++) { + row_start = mdb_pg_get_int16(mdb, (fmt->row_count_offset + 2) + (i*2)); + row_end = mdb_find_end_of_row(mdb, i); + row_size = row_end - row_start + 1; + pos -= row_size; + memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size); + _mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos); + } + + /* almost done, copy page over current */ + memcpy(mdb->pg_buf, new_pg, fmt->pg_size); + + g_free(new_pg); + + _mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb)); + if (mdb_get_option(MDB_DEBUG_WRITE)) { + buffer_dump(mdb->pg_buf, 0, 39); + buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1); + } + /* drum roll, please */ + if (!mdb_write_pg(mdb, table->cur_phys_pg)) { + fprintf(stderr, "write failed! exiting...\n"); + exit(1); + } + return 0; +} +static int +mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields) +/*, guint32 pgnum, guint16 rownum) +static int +mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg) +*/ +{ + MdbCatalogEntry *entry = table->entry; + MdbHandle *mdb = entry->mdb; + MdbColumn *col; + guint32 pg; + guint16 row; + unsigned char *new_pg; + unsigned char key_hash[256]; + unsigned char iflag; + int keycol; + + new_pg = mdb_new_leaf_pg(entry); + + mdb_index_page_reset(ipg); + mdb_read_pg(mdb, ipg->pg); + + /* do we support this index type yet? */ + if (idx->num_keys > 1) { + fprintf(stderr,"multikey indexes not yet supported, aborting\n"); + return 0; + } + keycol = idx->key_col_num[0]; + col = g_ptr_array_index (table->columns, keycol - 1); + printf("keycol = %d (%s)\n", keycol, col->name); + if (!mdb_is_fixed_col(col)) { + fprintf(stderr,"variable length key columns not yet supported, aborting\n"); + return 0; + } + printf("col size = %d\n", col->col_size); + + while (mdb_index_find_next_on_page(mdb, ipg)) { + + /* check for compressed indexes. */ + if (ipg->len < col->col_size + 1) { + fprintf(stderr,"compressed indexes not yet supported, aborting\n"); + return 0; + } + + pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4); + row = mdb->pg_buf[ipg->offset + ipg->len - 1]; + iflag = mdb->pg_buf[ipg->offset]; + mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash); + key_hash[col->col_size - 1] &= 0x7f; + printf("length = %d\n", ipg->len); + printf("iflag = %d pg = %" G_GUINT32_FORMAT + " row = %" G_GUINT16_FORMAT "\n", iflag, pg, row); + buffer_dump(mdb->pg_buf, ipg->offset, ipg->offset + ipg->len - 1); + buffer_dump(mdb->pg_buf, ipg->offset + 1, ipg->offset + col->col_size); + buffer_dump(key_hash, 0, col->col_size - 1); + ipg->offset += ipg->len; + ipg->len = 0; + row++; + } + g_free(new_pg); + + return ipg->len; +} diff --git a/map/poi_geodownload/poi_geodownload.c b/map/poi_geodownload/poi_geodownload.c new file mode 100644 index 00000000..5b698da3 --- /dev/null +++ b/map/poi_geodownload/poi_geodownload.c @@ -0,0 +1,775 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2009 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 <mdbtools.h> +#include "config.h" +#include "debug.h" +#include "coord.h" +#include "projection.h" +#include "item.h" +#include "map.h" +#include "plugin.h" + + +#include <stdio.h> +#include <sys/types.h> +#include <dirent.h> + +struct map_priv +{ + char *filename; + MdbHandle *h; + MdbHandle *h_idx; + MdbTableDef *table; + GPtrArray *table_col; + MdbColumn **cols; + MdbIndex *idx; + int idx_size; + enum item_type type; + int name_col; +}; + +struct map_rect_priv +{ + struct item item; + struct map_priv *m; + enum attr_type attr_next; + int cidx; + char buffer[4096]; +}; + +#if 0 +struct index_data { + unsigned char data[15]; +}; +struct poi { + char filename[1024]; + char icon[1024]; + long pos; + MdbHandle *h; + MdbHandle *h_idx; + MdbTableDef *table; + GPtrArray *table_col; + MdbColumn **cols; + MdbIndex *idx; + int idx_size; + struct index_data index_data; + MdbIndexChain chain; + struct poi *next; +} *poi_list; + +struct poi_data { + struct poi *poi; + int page; + int row; +}; + + char poipath[256]; + char poibmp[256]; + + +#endif + +static void +print_col(MdbHandle *h, MdbColumn *col, char *buffer, int hex) +{ + char *s; + dbg(1,"type=%d\n", col->col_type); + switch (col->col_type) { + case MDB_BOOL: + strcpy(buffer, mdb_pg_get_byte(h, col->cur_value_start) ? "True" : "False"); + break; + case MDB_BYTE: + sprintf(buffer, "%d", mdb_pg_get_byte(h, col->cur_value_start)); + break; + case MDB_LONGINT: + if (hex) + sprintf(buffer, "0x%lx", mdb_pg_get_int32(h, col->cur_value_start)); + else + sprintf(buffer, "%ld", mdb_pg_get_int32(h, col->cur_value_start)); + break; + case MDB_DOUBLE: + sprintf(buffer, "%f", mdb_pg_get_double(h, col->cur_value_start)); + break; + case MDB_TEXT: + dbg(1,"pg_buf %p start %d len %d\n", h->pg_buf, col->cur_value_start, col->cur_value_len); + if (col->cur_value_len) { + s=mdb_col_to_string (h, h->pg_buf, col->cur_value_start, col->col_type, col->cur_value_len); + dbg(1,"s=%p\n", s); + sprintf(buffer, "%s", s); + } + break; + default: + sprintf(buffer, "unknown (%d)", col->col_type); + } +} + +#if 0 + +static void +setup_idx_data(struct index_data *idx, struct coord *c, unsigned int geoflags, int size) +{ + /* 7f 80 1c 91 0a 7f 80 5c f5 41 7f 80 00 00 05 */ + idx->data[0]=0x7f; + idx->data[1]=(c->x >> 24) ^ 0x80; + idx->data[2]=c->x >> 16; + idx->data[3]=c->x >> 8; + idx->data[4]=c->x; + idx->data[5]=0x7f; + idx->data[6]=(c->y >> 24) ^ 0x80; + idx->data[7]=c->y >> 16; + idx->data[8]=c->y >> 8; + idx->data[9]=c->y; + idx->data[10]=0x7f; + if (size > 12) { + idx->data[11]=0x80 | (geoflags >> 24); + idx->data[12]=geoflags >> 16; + idx->data[13]=geoflags >> 8; + idx->data[14]=geoflags; + } else { + idx->data[11]=geoflags; + } +} + +static void +setup_idx_rect(struct coord *rect, struct index_data *idx, int size) +{ + struct coord r[2]; + r[0].x=rect[0].x; + r[0].y=rect[1].y; + r[1].x=rect[1].x; + r[1].y=rect[0].y; +#if 0 + printf("low 0x%x 0%x\n", r[0].x, r[0].y); + printf("high 0x%x 0%x\n", r[1].x, r[1].y); +#endif + setup_idx_data(idx, r, 0, size); + setup_idx_data(idx+1, r+1, 0xffffffff, size); +} + +#endif + +static int +load_row(struct map_priv *poi, int pg, int row) +{ + int row_start, row_end, offset; + unsigned int num_fields, i; + MdbField fields[256]; + MdbFormatConstants *fmt; + int debug=0; + + fmt=poi->h->fmt; + mdb_read_pg(poi->h, pg); + dbg(1, "enter poi=%p pg=%d row=%d\n", poi, pg, row); + dbg(1,"Page Type %d row_count_offset %d\n",poi->h->pg_buf[0], fmt->row_count_offset); + for (i = 0; i <= row; i++) { + offset=(fmt->row_count_offset + 2) + i * 2; + dbg(1,"row %d %d 0x%x\n", i, offset, mdb_pg_get_int16(poi->h, offset)); + } + row_start = mdb_pg_get_int16(poi->h, (fmt->row_count_offset + 2) + row * 2); + if (row_start & 0x4000) + return 1; + row_end = mdb_find_end_of_row(poi->h, row); + if (debug) { + printf("start=0x%x end=0x%x\n", row_start, row_end); + buffer_dump(poi->h->pg_buf, row_start, row_end); + } + + poi->h->cur_pos=row_start & 0x1fff; + poi->table->cur_row=row; + num_fields = mdb_crack_row(poi->table, row_start & 0x1fff, row_end, fields); + dbg(1,"num_fields=%d\n", num_fields); + for (i = 0; i < num_fields; i++) { + dbg(1,"i=%d/%d\n", i, num_fields); + poi->cols[i]->cur_value_start=fields[i].start; + poi->cols[i]->cur_value_len=fields[i].siz; + } + return 0; +} + +#if 0 + +static MdbIndexPage * +index_next_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain) +{ + MdbIndexPage *ipg; + + ipg = mdb_index_read_bottom_pg(mdb, idx, chain); + if (!mdb_index_find_next_on_page(mdb, ipg)) { +#if 0 + printf("no next\n"); +#endif + if (!chain->clean_up_mode) { +#if 0 + printf("no cleanup\n"); +#endif + if (!(ipg = mdb_index_unwind(mdb, idx, chain))) + chain->clean_up_mode = 1; + } + if (chain->clean_up_mode) { +#if 0 + printf("cleanup\n"); +#endif + //fprintf(stdout,"in cleanup mode\n"); + + if (!chain->last_leaf_found) { + printf("no last_leaf_found\n"); + return NULL; + } + mdb_read_pg(mdb, chain->last_leaf_found); + chain->last_leaf_found = + mdb_pg_get_int24(mdb, 0x0c); + //printf("next leaf %lu\n", chain->last_leaf_found); + mdb_read_pg(mdb, chain->last_leaf_found); + /* reuse the chain for cleanup mode */ + chain->cur_depth = 1; + ipg = &chain->pages[0]; + mdb_index_page_init(ipg); + ipg->pg = chain->last_leaf_found; + //printf("next on page %d\n", + if (!mdb_index_find_next_on_page(mdb, ipg)) { +#if 0 + printf("no find_next_on_page\n"); +#endif + return NULL; + } + } + } + return ipg; +} + +static int +index_next(struct poi *poi, struct index_data *idx) +{ + MdbIndexPage *ipg; + MdbIndexChain *chain = &poi->chain; + int row; + int pg; + int offset; + char *cmp, *low, *high; + int debug=0; + + + for(;;) { + for(;;) { + ipg=index_next_row(poi->h_idx, poi->idx, chain); + if (! ipg) + return 0; + row = poi->h_idx->pg_buf[ipg->offset + ipg->len - 1]; + pg = mdb_pg_get_int24_msb(poi->h_idx, ipg->offset + ipg->len - 4); + + offset=poi->idx_size+4-ipg->len; + memcpy(poi->index_data.data+offset, poi->h_idx->pg_buf+ipg->offset, ipg->len - 4); + cmp=poi->index_data.data; + low=idx[0].data; + high=idx[1].data; + if (debug > 1) { + buffer_dump(low, 0, poi->idx_size-1); + buffer_dump(cmp, 0, poi->idx_size-1); + buffer_dump(high, 0, poi->idx_size-1); + printf("%d %d %d\n", memcmp(cmp, low, poi->idx_size), memcmp(cmp, high, poi->idx_size), offset); + } +#if 0 + buffer_dump(poi->h_idx->pg_buf, ipg->offset, ipg->offset+ipg->len-1); +#endif + ipg->offset += ipg->len; + if (memcmp(cmp, low, poi->idx_size) >= 0) { + if (memcmp(cmp, high, poi->idx_size) <=0 ) { + if (debug) { + printf("match\n"); + buffer_dump(low, 0, poi->idx_size-1); + buffer_dump(cmp, 0, poi->idx_size-1); + buffer_dump(high, 0, poi->idx_size-1); + printf("%d %d %d\n", memcmp(cmp, low, poi->idx_size), memcmp(cmp, high, poi->idx_size), offset); + } + break; + } else { + return 0; + } + } + if (debug > 1) + printf("row=0x%x pg=0x%x len=%d\n", row, pg, ipg->len); + } + if (debug) + printf("match: row=0x%x pg=0x%x len=%d\n", row, pg, ipg->len); + if (!load_row(poi, pg, row)) + break; + } + return 1; +} + +#endif + +static int +load_poi_table(struct map_priv *m, MdbCatalogEntry *entry) +{ + int j; + MdbIndex *idx; + + m->h_idx=NULL; + m->table = mdb_read_table(entry); + m->table_col = mdb_read_columns(m->table); + mdb_read_indices(m->table); + m->cols = (MdbColumn **) (m->table_col->pdata); + if (m->table_col->len < 4 || strcasecmp(m->cols[0]->name, "X") || + strcasecmp(m->cols[1]->name, "Y") || strcasecmp(m->cols[3]->name, "GEOFLAGS")) + return 1; + m->name_col=-1; + for (j = 0; j < m->table_col->len ; j++) { + if (!strcasecmp(m->cols[j]->name, "NAME")) + m->name_col=j; + } + for (j = 0; j < m->table->num_idxs; j++) { + idx = m->table->indices->pdata[j]; + if (idx->num_keys == 3 && idx->key_col_num[0] == 1 && + idx->key_col_num[1] == 2 && idx->key_col_num[2] == 4) { + m->idx = idx; + m->idx_size=3+m->cols[0]->col_size+m->cols[1]->col_size+m->cols[3]->col_size; + m->h_idx=mdb_clone_handle(m->h); + } + } + return 0; +} + +#if 0 + +static void +load_poi(char *filename, char *icon, int type) +{ + int i; + MdbCatalogEntry *entry; + GPtrArray *catalog; + struct poi *new = g_new0(struct poi, 1); + + FILE *fp = fopen(filename,"r"); + if( fp ) { + fclose(fp); + } else { + printf("ERR : POI file %s does not exists!\n",filename); + exit(0); + return -1; + } + + + fp = fopen(icon,"r"); + if( fp ) { + fclose(fp); + } else { + printf("ERR : WARNING INCORRECT PICTURE! %s!\n",icon); + exit(0); + return -1; + } + + strcpy(new->filename,filename); + strcpy(new->icon,icon); + new->type = type; + + + if (type == 0) { + new->h = mdb_open(filename, MDB_NOFLAGS); + catalog = mdb_read_catalog(new->h, MDB_TABLE); + for (i = 0; i < catalog->len; i++) { + entry = catalog->pdata[i]; + if (!strcasecmp(entry->object_name, "_INDEXDATA")) { + if (load_poi_table(new, entry)) { + printf("%s invalid\n", filename); + g_free(new); + new=NULL; + } + } + } + g_ptr_array_free(catalog, 1); + } + if (new) { + new->next = poi_list; + poi_list = new; + } +} + +static void +get_coord(struct poi *p, struct coord *c) +{ + c->x=mdb_pg_get_int32(p->h, p->cols[0]->cur_value_start); + c->y=mdb_pg_get_int32(p->h, p->cols[1]->cur_value_start); +} + +static void +poi_info(struct display_list *list, struct popup_item **popup) +{ + struct poi_data *data=list->data; + struct poi *poi=data->poi; + struct popup_item *popup_last, *popup_val_last; + char *text,buffer[4096]; + int j; + MdbColumn *col; + char *v; + + popup_last = *popup; + + popup_val_last = NULL; + sprintf(buffer,"File:%s", poi->filename); + popup_item_new_text(&popup_val_last, buffer, 1); + sprintf(buffer,"Icon:%s", poi->icon); + popup_item_new_text(&popup_val_last, buffer, 2); + if (poi->type == 0) { + printf("poi_info pg=%d row=%d\n", data->page, data->row); + load_row(poi, data->page, data->row); + sprintf(buffer,"Page:%d", data->page); + popup_item_new_text(&popup_val_last, buffer, 3); + sprintf(buffer,"Row:%d", data->row); + popup_item_new_text(&popup_val_last, buffer, 4); + for (j = 0; j < poi->table_col->len; j++) { + col = poi->table_col->pdata[j]; + #if 0 + printf("start: %d type:%d\n", col->cur_value_start, col->col_type); + #endif + sprintf(buffer, "%s:", col->name); + v = buffer + strlen(buffer); + if (!strcasecmp(col->name,"X") || !strcasecmp(col->name,"Y")) + print_col(poi->h, col, v, 1); + else + print_col(poi->h, col, v, 0); + #if 0 + printf("%s\n", buffer); + #endif + text=g_convert(buffer,-1,"utf-8","iso8859-1",NULL,NULL,NULL); + popup_item_new_text(&popup_val_last, buffer, j+10); + g_free(text); + } + } + popup_item_new_text(&popup_last, "POI", 20)->submenu = popup_val_last; + *popup=popup_last; +} + +static void +draw_poi(struct poi *p, struct container *co, struct point *pnt) +{ + struct poi_data data; + data.poi=p; + if (p->type == 0) { + data.page=p->h->cur_pg; + data.row=p->table->cur_row-1; + } + if (p->type == 1) { + data.row=p->pos; + } + display_add(&co->disp[display_poi], 5, 0, p->icon, 1, pnt, poi_info, &data, sizeof(data)); +} + +static void +plugin_draw(struct container *co) +{ + struct coord c; + struct point pnt; + struct poi *p; + struct index_data idx[2]; + int use_index=0; + int debug=1; + + p = poi_list; + + if (co->trans->scale > 1024) + return; + if (debug) { + printf("scale=%ld\n", co->trans->scale); + printf("rect 0x%lx,0%lx-0x%lx,0x%lx\n", co->trans->rect[0].x, co->trans->rect[0].y, co->trans->rect[1].x, co->trans->rect[1].y); + } + while (p) { + if (p->type == 0) { + if (use_index) + setup_idx_rect(co->trans->rect, idx, p->idx_size); + if (! use_index) { + printf("rewind %s %p\n", p->filename, p->table); + mdb_rewind_table(p->table); + while (mdb_fetch_row(p->table)) { + get_coord(p, &c); + if (transform(co->trans, &c, &pnt)) { + if (debug) + printf("coord 0x%lx,0x%lx pg %d row %d\n", c.x, c.y, p->h->cur_pg, p->table->cur_row); + draw_poi(p, co, &pnt); + } + } + } else { + memset(&p->chain, 0, sizeof(p->chain)); + while (index_next(p, idx)) { + get_coord(p, &c); + if (transform(co->trans, &c, &pnt)) { + if (debug) + printf("coord 0x%lx,0x%lx pg %d row %d\n", c.x, c.y, p->h->cur_pg, p->table->cur_row); + draw_poi(p, co, &pnt); + } + } + } + } + if (p->type == 1) { + FILE *f; + char line[1024]; + struct text_poi tpoi; + if(!(f=fopen(p->filename, "r"))){ + printf("can't open poi file for drawing!\n"); + exit(0); + } +#if 0 + printf("opened poi file %s for drawing!\n",p->filename); +#endif + p->pos=ftell(f); + fgets(line, 1024, f); + while (!feof(f)) { + if (strlen(line)) { + line[strlen(line)-1]='\0'; + } + if (parse_text_poi(line, &tpoi)) { + transform_mercator(&tpoi.lat,&tpoi.lng,&c); +// printf("%ld %ld\n", c.x, c.y); + if (transform(co->trans, &c, &pnt)) { + draw_poi(p, co, &pnt); + } + } + p->pos=ftell(f); + fgets(line, 1024, f); + } + fclose(f); + } + p = p->next; + } + +} + +#endif + +static void +map_destroy_poi_geodownload(struct map_priv *m) +{ + dbg(1,"enter\n"); + g_free(m); +} + +static void +poi_geodownload_coord_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + mr->cidx=0; +} + + +static int +poi_geodownload_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + dbg(1,"enter\n"); + if (mr->cidx || !count) + return 0; + c->x=mdb_pg_get_int32(mr->m->h, mr->m->cols[0]->cur_value_start); + c->y=mdb_pg_get_int32(mr->m->h, mr->m->cols[1]->cur_value_start); + dbg(1,"x=0x%x y=0x%x\n", c->x, c->y); + return 1; +} + +static void +poi_geodownload_attr_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + mr->attr_next=attr_label; +} + +static int +poi_geodownload_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + struct map_priv *m=mr->m; + MdbColumn *col; + char *v; + int j; + + dbg(1,"enter\n"); + attr->type=attr_type; + switch (attr_type) { + case attr_any: + while (mr->attr_next != attr_none) { + if (poi_geodownload_attr_get(mr, mr->attr_next, attr)) + return 1; + } + return 0; + case attr_label: + mr->attr_next=attr_debug; + if (m->name_col == -1) + return 0; + col=m->cols[m->name_col]; + if (col->cur_value_len) + attr->u.str=mdb_col_to_string (m->h, m->h->pg_buf, col->cur_value_start, col->col_type, col->cur_value_len); + else + attr->u.str=""; + return 1; + case attr_debug: + mr->attr_next=attr_none; + v=mr->buffer; + *v='\0'; + for (j = 0; j < mr->m->table_col->len; j++) { + col = mr->m->table_col->pdata[j]; + printf("start: %d type:%d\n", col->cur_value_start, col->col_type); + sprintf(v, "%s:", col->name); + v += strlen(v); + if (!strcasecmp(col->name,"X") || !strcasecmp(col->name,"Y")) + print_col(mr->m->h, col, v, 1); + else + print_col(mr->m->h, col, v, 0); + v += strlen(v); + *v++='\n'; + *v='\0'; + } + attr->u.str=mr->buffer; + return 1; + default: + break; + } + return 0; +} + +static struct item_methods methods_poi_geodownload = { + poi_geodownload_coord_rewind, + poi_geodownload_coord_get, + poi_geodownload_attr_rewind, + poi_geodownload_attr_get, +}; + + +static struct map_rect_priv * +map_rect_new_poi_geodownload(struct map_priv *map, struct map_selection *sel) +{ + struct map_rect_priv *mr; + + dbg(1,"enter\n"); + mr=g_new0(struct map_rect_priv, 1); + mr->item.meth=&methods_poi_geodownload; + mr->item.id_hi=0; + mr->item.id_lo=0; + mr->item.priv_data=mr; + mr->item.type=map->type; + mr->m=map; + mdb_rewind_table(map->table); + return mr; +} + + +static void +map_rect_destroy_poi_geodownload(struct map_rect_priv *mr) +{ + g_free(mr); +} + +static struct item * +map_rect_get_item_poi_geodownload(struct map_rect_priv *mr) +{ + dbg(1,"enter\n"); + if (mdb_fetch_row(mr->m->table)) { + mr->item.id_hi=mr->m->table->cur_phys_pg; + mr->item.id_lo=mr->m->table->cur_row-1; + poi_geodownload_attr_rewind(mr); + return &mr->item; + } + return NULL; +} + +static struct item * +map_rect_get_item_byid_poi_geodownload(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + char *v, buffer[4096]; + int j; + MdbColumn *col; + + dbg(1,"enter\n"); + load_row(mr->m, id_hi, id_lo); + for (j = 0; j < mr->m->table_col->len; j++) { + col = mr->m->table_col->pdata[j]; + printf("start: %d type:%d\n", col->cur_value_start, col->col_type); + sprintf(buffer, "%s:", col->name); + v = buffer + strlen(buffer); + if (!strcasecmp(col->name,"X") || !strcasecmp(col->name,"Y")) + print_col(mr->m->h, col, v, 1); + else + print_col(mr->m->h, col, v, 0); + printf("%s\n", buffer); + } + dbg(1,"ret=%p\n", &mr->item); + poi_geodownload_attr_rewind(mr); + return &mr->item; +} + + +static struct map_methods map_methods_poi_geodownload = { + projection_mg, + "iso8859-1", + map_destroy_poi_geodownload, + map_rect_new_poi_geodownload, + map_rect_destroy_poi_geodownload, + map_rect_get_item_poi_geodownload, + map_rect_get_item_byid_poi_geodownload, +}; + +static struct map_priv * +map_new_poi_geodownload(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + MdbCatalogEntry *entry; + GPtrArray *catalog; + int i; + struct attr *attr; + struct attr *data=attr_search(attrs, NULL, attr_data); + char *filename; + if (! data) + return NULL; + filename=data->u.str; + dbg(1,"filename %s\n",filename); + *meth=map_methods_poi_geodownload; + + m=g_new(struct map_priv, 1); + m->filename=g_strdup(filename); + m->h = mdb_open(m->filename, MDB_NOFLAGS); + m->type=type_none; + dbg(1,"attr_search\n"); + attr=attr_search(attrs, NULL, attr_item_type); + dbg(1,"attr_search result %p\n", attr); + if (attr) + m->type=attr->u.item_type; + + + catalog = mdb_read_catalog(m->h, MDB_TABLE); + for (i = 0; i < catalog->len; i++) { + entry = catalog->pdata[i]; + dbg(1,"object name '%s'\n", entry->object_name); + if (!strcasecmp(entry->object_name, "_INDEXDATA")) { + if (m && load_poi_table(m, entry)) { + printf("%s invalid\n", filename); + g_free(m); + m=NULL; + } + } + } + g_ptr_array_free(catalog, 1); + return m; +} + +void +plugin_init(void) +{ + dbg(1,"plugin_init\n"); + plugin_register_map_type("poi_geodownload", map_new_poi_geodownload); + mdb_init(); +} + diff --git a/map/shapefile/Makefile.am b/map/shapefile/Makefile.am new file mode 100644 index 00000000..3483f440 --- /dev/null +++ b/map/shapefile/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_shapefile +if PLUGINS + modulemap_LTLIBRARIES = libmap_shapefile.la +else + noinst_LTLIBRARIES = libmap_shapefile.la +endif +libmap_shapefile_la_SOURCES = shapefile.c dbfopen.c shpopen.c shptree.c shapefil.h +libmap_shapefile_la_LDFLAGS = -module -avoid-version diff --git a/map/shapefile/dbfopen.c b/map/shapefile/dbfopen.c new file mode 100644 index 00000000..4f682eab --- /dev/null +++ b/map/shapefile/dbfopen.c @@ -0,0 +1,1871 @@ +/****************************************************************************** + * $Id: dbfopen.c,v 1.83 2008/11/12 14:28:15 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of .dbf access API documented in dbf_api.html. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: dbfopen.c,v $ + * Revision 1.83 2008/11/12 14:28:15 fwarmerdam + * DBFCreateField() now works on files with records + * + * Revision 1.82 2008/11/11 17:47:09 fwarmerdam + * added DBFDeleteField() function + * + * Revision 1.81 2008/01/03 17:48:13 bram + * in DBFCreate, use default code page LDID/87 (= 0x57, ANSI) + * instead of LDID/3. This seems to be the same as what ESRI + * would be doing by default. + * + * Revision 1.80 2007/12/30 14:36:39 fwarmerdam + * avoid syntax issue with last comment. + * + * Revision 1.79 2007/12/30 14:35:48 fwarmerdam + * Avoid char* / unsigned char* warnings. + * + * Revision 1.78 2007/12/18 18:28:07 bram + * - create hook for client specific atof (bugzilla ticket 1615) + * - check for NULL handle before closing cpCPG file, and close after reading. + * + * Revision 1.77 2007/12/15 20:25:21 bram + * dbfopen.c now reads the Code Page information from the DBF file, and exports + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/<number>") or as the + * content of an accompanying .CPG file. When creating a DBF file, the code can + * be set using DBFCreateEx. + * + * Revision 1.76 2007/12/12 22:21:32 bram + * DBFClose: check for NULL psDBF handle before trying to close it. + * + * Revision 1.75 2007/12/06 13:58:19 fwarmerdam + * make sure file offset calculations are done in as SAOffset + * + * Revision 1.74 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.73 2007/09/03 19:48:11 fwarmerdam + * move DBFReadAttribute() static dDoubleField into dbfinfo + * + * Revision 1.72 2007/09/03 19:34:06 fwarmerdam + * Avoid use of static tuple buffer in DBFReadTuple() + * + * Revision 1.71 2006/06/22 14:37:18 fwarmerdam + * avoid memory leak if dbfopen fread fails + * + * Revision 1.70 2006/06/17 17:47:05 fwarmerdam + * use calloc() for dbfinfo in DBFCreate + * + * Revision 1.69 2006/06/17 15:34:32 fwarmerdam + * disallow creating fields wider than 255 + * + * Revision 1.68 2006/06/17 15:12:40 fwarmerdam + * Fixed C++ style comments. + * + * Revision 1.67 2006/06/17 00:24:53 fwarmerdam + * Don't treat non-zero decimals values as high order byte for length + * for strings. It causes serious corruption for some files. + * http://bugzilla.remotesensing.org/show_bug.cgi?id=1202 + * + * Revision 1.66 2006/03/29 18:26:20 fwarmerdam + * fixed bug with size of pachfieldtype in dbfcloneempty + * + * Revision 1.65 2006/02/15 01:14:30 fwarmerdam + * added DBFAddNativeFieldType + * + * Revision 1.64 2006/02/09 00:29:04 fwarmerdam + * Changed to put spaces into string fields that are NULL as + * per http://bugzilla.maptools.org/show_bug.cgi?id=316. + * + * Revision 1.63 2006/01/25 15:35:43 fwarmerdam + * check success on DBFFlushRecord + * + * Revision 1.62 2006/01/10 16:28:03 fwarmerdam + * Fixed typo in CPLError. + * + * Revision 1.61 2006/01/10 16:26:29 fwarmerdam + * Push loading record buffer into DBFLoadRecord. + * Implement CPL error reporting if USE_CPL defined. + * + * Revision 1.60 2006/01/05 01:27:27 fwarmerdam + * added dbf deletion mark/fetch + * + * Revision 1.59 2005/03/14 15:20:28 fwarmerdam + * Fixed last change. + * + * Revision 1.58 2005/03/14 15:18:54 fwarmerdam + * Treat very wide fields with no decimals as double. This is + * more than 32bit integer fields. + * + * Revision 1.57 2005/02/10 20:16:54 fwarmerdam + * Make the pszStringField buffer for DBFReadAttribute() static char [256] + * as per bug 306. + * + * Revision 1.56 2005/02/10 20:07:56 fwarmerdam + * Fixed bug 305 in DBFCloneEmpty() - header length problem. + * + * Revision 1.55 2004/09/26 20:23:46 fwarmerdam + * avoid warnings with rcsid and signed/unsigned stuff + * + * Revision 1.54 2004/09/15 16:26:10 fwarmerdam + * Treat all blank numeric fields as null too. + */ + +#include "shapefil.h" + +#include <math.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +SHP_CVSID("$Id: dbfopen.c,v 1.83 2008/11/12 14:28:15 fwarmerdam Exp $") + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* DBFWriteHeader() */ +/* */ +/* This is called to write out the file header, and field */ +/* descriptions before writing any actual data records. This */ +/* also computes all the DBFDataSet field offset/size/decimals */ +/* and so forth values. */ +/************************************************************************/ + +static void DBFWriteHeader(DBFHandle psDBF) + +{ + unsigned char abyHeader[XBASE_FLDHDR_SZ]; + int i; + + if( !psDBF->bNoHeader ) + return; + + psDBF->bNoHeader = FALSE; + +/* -------------------------------------------------------------------- */ +/* Initialize the file header information. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) + abyHeader[i] = 0; + + abyHeader[0] = 0x03; /* memo field? - just copying */ + + /* write out a dummy date */ + abyHeader[1] = 95; /* YY */ + abyHeader[2] = 7; /* MM */ + abyHeader[3] = 26; /* DD */ + + /* record count preset at zero */ + + abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256); + abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256); + + abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256); + abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256); + + abyHeader[29] = (unsigned char) (psDBF->iLanguageDriver); + +/* -------------------------------------------------------------------- */ +/* Write the initial 32 byte file header, and all the field */ +/* descriptions. */ +/* -------------------------------------------------------------------- */ + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FWrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); + psDBF->sHooks.FWrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, + psDBF->fp ); + +/* -------------------------------------------------------------------- */ +/* Write out the newline character if there is room for it. */ +/* -------------------------------------------------------------------- */ + if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) + { + char cNewline; + + cNewline = 0x0d; + psDBF->sHooks.FWrite( &cNewline, 1, 1, psDBF->fp ); + } +} + +/************************************************************************/ +/* DBFFlushRecord() */ +/* */ +/* Write out the current record if there is one. */ +/************************************************************************/ + +static int DBFFlushRecord( DBFHandle psDBF ) + +{ + SAOffset nRecordOffset; + + if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) + { + psDBF->bCurrentRecordModified = FALSE; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) psDBF->nCurrentRecord + + psDBF->nHeaderLength; + + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ) != 0 + || psDBF->sHooks.FWrite( psDBF->pszCurrentRecord, + psDBF->nRecordLength, + 1, psDBF->fp ) != 1 ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_FileIO, + "Failure writing DBF record %d.", + psDBF->nCurrentRecord ); +#else + fprintf( stderr, "Failure writing DBF record %d.", + psDBF->nCurrentRecord ); +#endif + return FALSE; + } + } + + return TRUE; +} + +/************************************************************************/ +/* DBFLoadRecord() */ +/************************************************************************/ + +static int DBFLoadRecord( DBFHandle psDBF, int iRecord ) + +{ + if( psDBF->nCurrentRecord != iRecord ) + { + SAOffset nRecordOffset; + + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + if( psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, SEEK_SET ) != 0 ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_FileIO, + "fseek(%ld) failed on DBF file.\n", + (long) nRecordOffset ); +#else + fprintf( stderr, "fseek(%ld) failed on DBF file.\n", + (long) nRecordOffset ); +#endif + return FALSE; + } + + if( psDBF->sHooks.FRead( psDBF->pszCurrentRecord, + psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_FileIO, + "fread(%d) failed on DBF file.\n", + psDBF->nRecordLength ); +#else + fprintf( stderr, "fread(%d) failed on DBF file.\n", + psDBF->nRecordLength ); +#endif + return FALSE; + } + + psDBF->nCurrentRecord = iRecord; + } + + return TRUE; +} + +/************************************************************************/ +/* DBFUpdateHeader() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFUpdateHeader( DBFHandle psDBF ) + +{ + unsigned char abyFileHeader[32]; + + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FRead( abyFileHeader, 32, 1, psDBF->fp ); + + abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256); + abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256); + abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256); + abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256); + + psDBF->sHooks.FSeek( psDBF->fp, 0, 0 ); + psDBF->sHooks.FWrite( abyFileHeader, 32, 1, psDBF->fp ); + + psDBF->sHooks.FFlush( psDBF->fp ); +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFOpen( const char * pszFilename, const char * pszAccess ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return DBFOpenLL( pszFilename, pszAccess, &sHooks ); +} + +/************************************************************************/ +/* DBFOpen() */ +/* */ +/* Open a .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFOpenLL( const char * pszFilename, const char * pszAccess, SAHooks *psHooks ) + +{ + DBFHandle psDBF; + SAFile pfCPG; + unsigned char *pabyBuf; + int nFields, nHeadLen, iField, i; + char *pszBasename, *pszFullname; + int nBufSize = 500; + +/* -------------------------------------------------------------------- */ +/* We only allow the access strings "rb" and "r+". */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 + && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 + && strcmp(pszAccess,"r+b") != 0 ) + return( NULL ); + + if( strcmp(pszAccess,"r") == 0 ) + pszAccess = "rb"; + + if( strcmp(pszAccess,"r+") == 0 ) + pszAccess = "rb+"; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszFilename)+5); + strcpy( pszBasename, pszFilename ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.dbf", pszBasename ); + + psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); + psDBF->fp = psHooks->FOpen( pszFullname, pszAccess ); + memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); + + if( psDBF->fp == NULL ) + { + sprintf( pszFullname, "%s.DBF", pszBasename ); + psDBF->fp = psDBF->sHooks.FOpen(pszFullname, pszAccess ); + } + + sprintf( pszFullname, "%s.cpg", pszBasename ); + pfCPG = psHooks->FOpen( pszFullname, "r" ); + if( pfCPG == NULL ) + { + sprintf( pszFullname, "%s.CPG", pszBasename ); + pfCPG = psHooks->FOpen( pszFullname, "r" ); + } + + free( pszBasename ); + free( pszFullname ); + + if( psDBF->fp == NULL ) + { + free( psDBF ); + if( pfCPG ) psHooks->FClose( pfCPG ); + return( NULL ); + } + + psDBF->bNoHeader = FALSE; + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + +/* -------------------------------------------------------------------- */ +/* Read Table Header info */ +/* -------------------------------------------------------------------- */ + pabyBuf = (unsigned char *) malloc(nBufSize); + if( psDBF->sHooks.FRead( pabyBuf, 32, 1, psDBF->fp ) != 1 ) + { + psDBF->sHooks.FClose( psDBF->fp ); + if( pfCPG ) psDBF->sHooks.FClose( pfCPG ); + free( pabyBuf ); + free( psDBF ); + return NULL; + } + + psDBF->nRecords = + pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; + + psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; + psDBF->nRecordLength = pabyBuf[10] + pabyBuf[11]*256; + psDBF->iLanguageDriver = pabyBuf[29]; + + psDBF->nFields = nFields = (nHeadLen - 32) / 32; + + psDBF->pszCurrentRecord = (char *) malloc(psDBF->nRecordLength); + +/* -------------------------------------------------------------------- */ +/* Figure out the code page from the LDID and CPG */ +/* -------------------------------------------------------------------- */ + + psDBF->pszCodePage = NULL; + if( pfCPG ) + { + size_t n; + char *buffer = (char *) pabyBuf; + buffer[0] = '\0'; + psDBF->sHooks.FRead( pabyBuf, nBufSize - 1, 1, pfCPG ); + n = strcspn( (char *) pabyBuf, "\n\r" ); + if( n > 0 ) + { + pabyBuf[n] = '\0'; + psDBF->pszCodePage = (char *) malloc(n + 1); + memcpy( psDBF->pszCodePage, pabyBuf, n + 1 ); + } + psDBF->sHooks.FClose( pfCPG ); + } + if( psDBF->pszCodePage == NULL && pabyBuf[29] != 0 ) + { + sprintf( (char *) pabyBuf, "LDID/%d", psDBF->iLanguageDriver ); + psDBF->pszCodePage = (char *) malloc(strlen((char*)pabyBuf) + 1); + strcpy( psDBF->pszCodePage, (char *) pabyBuf ); + } + +/* -------------------------------------------------------------------- */ +/* Read in Field Definitions */ +/* -------------------------------------------------------------------- */ + + pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); + psDBF->pszHeader = (char *) pabyBuf; + + psDBF->sHooks.FSeek( psDBF->fp, 32, 0 ); + if( psDBF->sHooks.FRead( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) + { + psDBF->sHooks.FClose( psDBF->fp ); + free( pabyBuf ); + free( psDBF->pszCurrentRecord ); + free( psDBF ); + return NULL; + } + + psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); + psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields); + psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields); + + for( iField = 0; iField < nFields; iField++ ) + { + unsigned char *pabyFInfo; + + pabyFInfo = pabyBuf+iField*32; + + if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = pabyFInfo[17]; + } + else + { + psDBF->panFieldSize[iField] = pabyFInfo[16]; + psDBF->panFieldDecimals[iField] = 0; + +/* +** The following seemed to be used sometimes to handle files with long +** string fields, but in other cases (such as bug 1202) the decimals field +** just seems to indicate some sort of preferred formatting, not very +** wide fields. So I have disabled this code. FrankW. + psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256; + psDBF->panFieldDecimals[iField] = 0; +*/ + } + + psDBF->pachFieldType[iField] = (char) pabyFInfo[11]; + if( iField == 0 ) + psDBF->panFieldOffset[iField] = 1; + else + psDBF->panFieldOffset[iField] = + psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; + } + + return( psDBF ); +} + +/************************************************************************/ +/* DBFClose() */ +/************************************************************************/ + +void SHPAPI_CALL +DBFClose(DBFHandle psDBF) +{ + if( psDBF == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Write out header if not already written. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bNoHeader ) + DBFWriteHeader( psDBF ); + + DBFFlushRecord( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Update last access date, and number of records if we have */ +/* write access. */ +/* -------------------------------------------------------------------- */ + if( psDBF->bUpdated ) + DBFUpdateHeader( psDBF ); + +/* -------------------------------------------------------------------- */ +/* Close, and free resources. */ +/* -------------------------------------------------------------------- */ + psDBF->sHooks.FClose( psDBF->fp ); + + if( psDBF->panFieldOffset != NULL ) + { + free( psDBF->panFieldOffset ); + free( psDBF->panFieldSize ); + free( psDBF->panFieldDecimals ); + free( psDBF->pachFieldType ); + } + + if( psDBF->pszWorkField != NULL ) + free( psDBF->pszWorkField ); + + free( psDBF->pszHeader ); + free( psDBF->pszCurrentRecord ); + free( psDBF->pszCodePage ); + + free( psDBF ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file with default code page LDID/87 (0x57) */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreate( const char * pszFilename ) + +{ + return DBFCreateEx( pszFilename, "LDID/87" ); // 0x57 +} + +/************************************************************************/ +/* DBFCreateEx() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreateEx( const char * pszFilename, const char* pszCodePage ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return DBFCreateLL( pszFilename, pszCodePage , &sHooks ); +} + +/************************************************************************/ +/* DBFCreate() */ +/* */ +/* Create a new .dbf file. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCreateLL( const char * pszFilename, const char * pszCodePage, SAHooks *psHooks ) + +{ + DBFHandle psDBF; + SAFile fp; + char *pszFullname, *pszBasename; + int i, ldid = -1; + char chZero = '\0'; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszFilename)+5); + strcpy( pszBasename, pszFilename ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.dbf", pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Create the file. */ +/* -------------------------------------------------------------------- */ + fp = psHooks->FOpen( pszFullname, "wb" ); + if( fp == NULL ) + return( NULL ); + + psHooks->FWrite( &chZero, 1, 1, fp ); + psHooks->FClose( fp ); + + fp = psHooks->FOpen( pszFullname, "rb+" ); + if( fp == NULL ) + return( NULL ); + + + sprintf( pszFullname, "%s.cpg", pszBasename ); + if( pszCodePage != NULL ) + { + if( strncmp( pszCodePage, "LDID/", 5 ) == 0 ) + { + ldid = atoi( pszCodePage + 5 ); + if( ldid > 255 ) + ldid = -1; // don't use 0 to indicate out of range as LDID/0 is a valid one + } + if( ldid < 0 ) + { + SAFile fpCPG = psHooks->FOpen( pszFullname, "w" ); + psHooks->FWrite( (char*) pszCodePage, strlen(pszCodePage), 1, fpCPG ); + psHooks->FClose( fpCPG ); + } + } + if( pszCodePage == NULL || ldid >= 0 ) + { + psHooks->Remove( pszFullname ); + } + + free( pszBasename ); + free( pszFullname ); + +/* -------------------------------------------------------------------- */ +/* Create the info structure. */ +/* -------------------------------------------------------------------- */ + psDBF = (DBFHandle) calloc(1,sizeof(DBFInfo)); + + memcpy( &(psDBF->sHooks), psHooks, sizeof(SAHooks) ); + psDBF->fp = fp; + psDBF->nRecords = 0; + psDBF->nFields = 0; + psDBF->nRecordLength = 1; + psDBF->nHeaderLength = 33; + + psDBF->panFieldOffset = NULL; + psDBF->panFieldSize = NULL; + psDBF->panFieldDecimals = NULL; + psDBF->pachFieldType = NULL; + psDBF->pszHeader = NULL; + + psDBF->nCurrentRecord = -1; + psDBF->bCurrentRecordModified = FALSE; + psDBF->pszCurrentRecord = NULL; + + psDBF->bNoHeader = TRUE; + + psDBF->iLanguageDriver = ldid > 0 ? ldid : 0; + psDBF->pszCodePage = NULL; + if( pszCodePage ) + { + psDBF->pszCodePage = (char * ) malloc( strlen(pszCodePage) + 1 ); + strcpy( psDBF->pszCodePage, pszCodePage ); + } + + return( psDBF ); +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf or to an existing one */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAddField(DBFHandle psDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ) + +{ + char chNativeType = 'C'; + + if( eType == FTLogical ) + chNativeType = 'L'; + else if( eType == FTString ) + chNativeType = 'C'; + else + chNativeType = 'N'; + + return DBFAddNativeFieldType( psDBF, pszFieldName, chNativeType, + nWidth, nDecimals ); +} + +/************************************************************************/ +/* DBFAddField() */ +/* */ +/* Add a field to a newly created .dbf file before any records */ +/* are written. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFAddNativeFieldType(DBFHandle psDBF, const char * pszFieldName, + char chType, int nWidth, int nDecimals ) + +{ + char *pszFInfo; + int i; + int nOldRecordLength, nOldHeaderLength; + char *pszRecord; + char chFieldFill; + SAOffset nRecordOffset; + +/* -------------------------------------------------------------------- */ +/* Do some checking to ensure we can add records to this file. */ +/* -------------------------------------------------------------------- */ + if( nWidth < 1 ) + return -1; + + if( nWidth > 255 ) + nWidth = 255; + + nOldRecordLength = psDBF->nRecordLength; + nOldHeaderLength = psDBF->nHeaderLength; + +/* -------------------------------------------------------------------- */ +/* SfRealloc all the arrays larger to hold the additional field */ +/* information. */ +/* -------------------------------------------------------------------- */ + psDBF->nFields++; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + +/* -------------------------------------------------------------------- */ +/* Assign the new field information fields. */ +/* -------------------------------------------------------------------- */ + psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength; + psDBF->nRecordLength += nWidth; + psDBF->panFieldSize[psDBF->nFields-1] = nWidth; + psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; + psDBF->pachFieldType[psDBF->nFields-1] = chType; + +/* -------------------------------------------------------------------- */ +/* Extend the required header information. */ +/* -------------------------------------------------------------------- */ + psDBF->nHeaderLength += 32; + psDBF->bUpdated = FALSE; + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); + + for( i = 0; i < 32; i++ ) + pszFInfo[i] = '\0'; + + if( (int) strlen(pszFieldName) < 10 ) + strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); + else + strncpy( pszFInfo, pszFieldName, 10); + + pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; + + if( chType == 'C' ) + { + pszFInfo[16] = (unsigned char) (nWidth % 256); + pszFInfo[17] = (unsigned char) (nWidth / 256); + } + else + { + pszFInfo[16] = (unsigned char) nWidth; + pszFInfo[17] = (unsigned char) nDecimals; + } + +/* -------------------------------------------------------------------- */ +/* Make the current record buffer appropriately larger. */ +/* -------------------------------------------------------------------- */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + /* we're done if dealing with new .dbf */ + if( psDBF->bNoHeader ) + return( psDBF->nFields - 1 ); + +/* -------------------------------------------------------------------- */ +/* For existing .dbf file, shift records */ +/* -------------------------------------------------------------------- */ + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * psDBF->nRecordLength); + + switch (chType) + { + case 'N': + case 'F': + chFieldFill = '*'; + break; + case 'D': + chFieldFill = '0'; + break; + case 'L': + chFieldFill = '?'; + break; + default: + chFieldFill = ' '; + break; + } + + for (i = psDBF->nRecords-1; i >= 0; --i) + { + nRecordOffset = nOldRecordLength * (SAOffset) i + nOldHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + /* set new field's value to NULL */ + memset(pszRecord + nOldRecordLength, chFieldFill, nWidth); + + nRecordOffset = psDBF->nRecordLength * (SAOffset) i + psDBF->nHeaderLength; + + /* move record to the new place*/ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, psDBF->nRecordLength, 1, psDBF->fp ); + } + + /* free record */ + free(pszRecord); + + /* force update of header with new header, record length and new field */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + return( psDBF->nFields-1 ); +} + +/************************************************************************/ +/* DBFReadAttribute() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, + char chReqType ) + +{ + unsigned char *pabyRec; + void *pReturnField = NULL; + +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( iField < 0 || iField >= psDBF->nFields ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return NULL; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Ensure we have room to extract the target field. */ +/* -------------------------------------------------------------------- */ + if( psDBF->panFieldSize[iField] >= psDBF->nWorkFieldLength ) + { + psDBF->nWorkFieldLength = psDBF->panFieldSize[iField] + 100; + if( psDBF->pszWorkField == NULL ) + psDBF->pszWorkField = (char *) malloc(psDBF->nWorkFieldLength); + else + psDBF->pszWorkField = (char *) realloc(psDBF->pszWorkField, + psDBF->nWorkFieldLength); + } + +/* -------------------------------------------------------------------- */ +/* Extract the requested field. */ +/* -------------------------------------------------------------------- */ + strncpy( psDBF->pszWorkField, + ((const char *) pabyRec) + psDBF->panFieldOffset[iField], + psDBF->panFieldSize[iField] ); + psDBF->pszWorkField[psDBF->panFieldSize[iField]] = '\0'; + + pReturnField = psDBF->pszWorkField; + +/* -------------------------------------------------------------------- */ +/* Decode the field. */ +/* -------------------------------------------------------------------- */ + if( chReqType == 'N' ) + { + psDBF->dfDoubleField = psDBF->sHooks.Atof(psDBF->pszWorkField); + + pReturnField = &(psDBF->dfDoubleField); + } + +/* -------------------------------------------------------------------- */ +/* Should we trim white space off the string attribute value? */ +/* -------------------------------------------------------------------- */ +#ifdef TRIM_DBF_WHITESPACE + else + { + char *pchSrc, *pchDst; + + pchDst = pchSrc = psDBF->pszWorkField; + while( *pchSrc == ' ' ) + pchSrc++; + + while( *pchSrc != '\0' ) + *(pchDst++) = *(pchSrc++); + *pchDst = '\0'; + + while( pchDst != psDBF->pszWorkField && *(--pchDst) == ' ' ) + *pchDst = '\0'; + } +#endif + + return( pReturnField ); +} + +/************************************************************************/ +/* DBFReadIntAttribute() */ +/* */ +/* Read an integer attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + if( pdValue == NULL ) + return 0; + else + return( (int) *pdValue ); +} + +/************************************************************************/ +/* DBFReadDoubleAttribute() */ +/* */ +/* Read a double attribute. */ +/************************************************************************/ + +double SHPAPI_CALL +DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + double *pdValue; + + pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); + + if( pdValue == NULL ) + return 0.0; + else + return( *pdValue ); +} + +/************************************************************************/ +/* DBFReadStringAttribute() */ +/* */ +/* Read a string attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); +} + +/************************************************************************/ +/* DBFReadLogicalAttribute() */ +/* */ +/* Read a logical attribute. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); +} + +/************************************************************************/ +/* DBFIsAttributeNULL() */ +/* */ +/* Return TRUE if value for field is NULL. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) + +{ + const char *pszValue; + int i; + + pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); + + if( pszValue == NULL ) + return TRUE; + + switch(psDBF->pachFieldType[iField]) + { + case 'N': + case 'F': + /* + ** We accept all asterisks or all blanks as NULL + ** though according to the spec I think it should be all + ** asterisks. + */ + if( pszValue[0] == '*' ) + return TRUE; + + for( i = 0; pszValue[i] != '\0'; i++ ) + { + if( pszValue[i] != ' ' ) + return FALSE; + } + return TRUE; + + case 'D': + /* NULL date fields have value "00000000" */ + return strncmp(pszValue,"00000000",8) == 0; + + case 'L': + /* NULL boolean fields have value "?" */ + return pszValue[0] == '?'; + + default: + /* empty string fields are considered NULL */ + return strlen(pszValue) == 0; + } +} + +/************************************************************************/ +/* DBFGetFieldCount() */ +/* */ +/* Return the number of fields in this table. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldCount( DBFHandle psDBF ) + +{ + return( psDBF->nFields ); +} + +/************************************************************************/ +/* DBFGetRecordCount() */ +/* */ +/* Return the number of records in this table. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetRecordCount( DBFHandle psDBF ) + +{ + return( psDBF->nRecords ); +} + +/************************************************************************/ +/* DBFGetFieldInfo() */ +/* */ +/* Return any requested information about the field. */ +/************************************************************************/ + +DBFFieldType SHPAPI_CALL +DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, + int * pnWidth, int * pnDecimals ) + +{ + if( iField < 0 || iField >= psDBF->nFields ) + return( FTInvalid ); + + if( pnWidth != NULL ) + *pnWidth = psDBF->panFieldSize[iField]; + + if( pnDecimals != NULL ) + *pnDecimals = psDBF->panFieldDecimals[iField]; + + if( pszFieldName != NULL ) + { + int i; + + strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); + pszFieldName[11] = '\0'; + for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) + pszFieldName[i] = '\0'; + } + + if ( psDBF->pachFieldType[iField] == 'L' ) + return( FTLogical); + + else if( psDBF->pachFieldType[iField] == 'N' + || psDBF->pachFieldType[iField] == 'F' ) + { + if( psDBF->panFieldDecimals[iField] > 0 + || psDBF->panFieldSize[iField] > 10 ) + return( FTDouble ); + else + return( FTInteger ); + } + else + { + return( FTString ); + } +} + +/************************************************************************/ +/* DBFWriteAttribute() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int i, j, nRetResult = TRUE; + unsigned char *pabyRec; + char szSField[400], szFormat[20]; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Translate NULL value to valid DBF file representation. */ +/* */ +/* Contributed by Jim Matthews. */ +/* -------------------------------------------------------------------- */ + if( pValue == NULL ) + { + switch(psDBF->pachFieldType[iField]) + { + case 'N': + case 'F': + /* NULL numeric fields have value "****************" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', + psDBF->panFieldSize[iField] ); + break; + + case 'D': + /* NULL date fields have value "00000000" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', + psDBF->panFieldSize[iField] ); + break; + + case 'L': + /* NULL boolean fields have value "?" */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', + psDBF->panFieldSize[iField] ); + break; + + default: + /* empty string fields are considered NULL */ + memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), ' ', + psDBF->panFieldSize[iField] ); + break; + } + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + switch( psDBF->pachFieldType[iField] ) + { + case 'D': + case 'N': + case 'F': + if( psDBF->panFieldDecimals[iField] == 0 ) + { + int nWidth = psDBF->panFieldSize[iField]; + + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + + sprintf( szFormat, "%%%dd", nWidth ); + sprintf(szSField, szFormat, (int) *((double *) pValue) ); + if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + } + else + { + int nWidth = psDBF->panFieldSize[iField]; + + if( (int) sizeof(szSField)-2 < nWidth ) + nWidth = sizeof(szSField)-2; + + sprintf( szFormat, "%%%d.%df", + nWidth, psDBF->panFieldDecimals[iField] ); + sprintf(szSField, szFormat, *((double *) pValue) ); + if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) + { + szSField[psDBF->panFieldSize[iField]] = '\0'; + nRetResult = FALSE; + } + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + szSField, strlen(szSField) ); + } + break; + + case 'L': + if (psDBF->panFieldSize[iField] >= 1 && + (*(char*)pValue == 'F' || *(char*)pValue == 'T')) + *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; + break; + + default: + if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + { + j = psDBF->panFieldSize[iField]; + nRetResult = FALSE; + } + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + break; + } + + return( nRetResult ); +} + +/************************************************************************/ +/* DBFWriteAttributeDirectly() */ +/* */ +/* Write an attribute record to the file, but without any */ +/* reformatting based on type. The provided buffer is written */ +/* as is to the field position in the record. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ) + +{ + int i, j; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + +/* -------------------------------------------------------------------- */ +/* Assign all the record fields. */ +/* -------------------------------------------------------------------- */ + if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] ) + j = psDBF->panFieldSize[iField]; + else + { + memset( pabyRec+psDBF->panFieldOffset[iField], ' ', + psDBF->panFieldSize[iField] ); + j = strlen((char *) pValue); + } + + strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), + (char *) pValue, j ); + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFWriteDoubleAttribute() */ +/* */ +/* Write a double attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, + double dValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteIntegerAttribute() */ +/* */ +/* Write a integer attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, + int nValue ) + +{ + double dValue = nValue; + + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); +} + +/************************************************************************/ +/* DBFWriteStringAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, + const char * pszValue ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); +} + +/************************************************************************/ +/* DBFWriteNULLAttribute() */ +/* */ +/* Write a string attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField ) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) ); +} + +/************************************************************************/ +/* DBFWriteLogicalAttribute() */ +/* */ +/* Write a logical attribute. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField, + const char lValue) + +{ + return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) ); +} + +/************************************************************************/ +/* DBFWriteTuple() */ +/* */ +/* Write an attribute record to the file. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ) + +{ + int i; + unsigned char *pabyRec; + +/* -------------------------------------------------------------------- */ +/* Is this a valid record? */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity > psDBF->nRecords ) + return( FALSE ); + + if( psDBF->bNoHeader ) + DBFWriteHeader(psDBF); + +/* -------------------------------------------------------------------- */ +/* Is this a brand new record? */ +/* -------------------------------------------------------------------- */ + if( hEntity == psDBF->nRecords ) + { + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + psDBF->nRecords++; + for( i = 0; i < psDBF->nRecordLength; i++ ) + psDBF->pszCurrentRecord[i] = ' '; + + psDBF->nCurrentRecord = hEntity; + } + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, hEntity ) ) + return FALSE; + + pabyRec = (unsigned char *) psDBF->pszCurrentRecord; + + memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength ); + + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + + return( TRUE ); +} + +/************************************************************************/ +/* DBFReadTuple() */ +/* */ +/* Read a complete record. Note that the result is only valid */ +/* till the next record read for any reason. */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFReadTuple(DBFHandle psDBF, int hEntity ) + +{ + if( hEntity < 0 || hEntity >= psDBF->nRecords ) + return( NULL ); + + if( !DBFLoadRecord( psDBF, hEntity ) ) + return NULL; + + return (const char *) psDBF->pszCurrentRecord; +} + +/************************************************************************/ +/* DBFCloneEmpty() */ +/* */ +/* Read one of the attribute fields of a record. */ +/************************************************************************/ + +DBFHandle SHPAPI_CALL +DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) +{ + DBFHandle newDBF; + + newDBF = DBFCreateEx ( pszFilename, psDBF->pszCodePage ); + if ( newDBF == NULL ) return ( NULL ); + + newDBF->nFields = psDBF->nFields; + newDBF->nRecordLength = psDBF->nRecordLength; + newDBF->nHeaderLength = psDBF->nHeaderLength; + + newDBF->pszHeader = (char *) malloc ( newDBF->nHeaderLength ); + memcpy ( newDBF->pszHeader, psDBF->pszHeader, newDBF->nHeaderLength ); + + newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields ); + memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + newDBF->pachFieldType = (char *) malloc ( sizeof(char) * psDBF->nFields ); + memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(char)*psDBF->nFields ); + + newDBF->bNoHeader = TRUE; + newDBF->bUpdated = TRUE; + + DBFWriteHeader ( newDBF ); + DBFClose ( newDBF ); + + newDBF = DBFOpen ( pszFilename, "rb+" ); + + return ( newDBF ); +} + +/************************************************************************/ +/* DBFGetNativeFieldType() */ +/* */ +/* Return the DBase field type for the specified field. */ +/* */ +/* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */ +/* 'N' (Numeric, with or without decimal), */ +/* 'L' (Logical), */ +/* 'M' (Memo: 10 digits .DBT block ptr) */ +/************************************************************************/ + +char SHPAPI_CALL +DBFGetNativeFieldType( DBFHandle psDBF, int iField ) + +{ + if( iField >=0 && iField < psDBF->nFields ) + return psDBF->pachFieldType[iField]; + + return ' '; +} + +/************************************************************************/ +/* str_to_upper() */ +/************************************************************************/ + +static void str_to_upper (char *string) +{ + int len; + short i = -1; + + len = strlen (string); + + while (++i < len) + if (isalpha(string[i]) && islower(string[i])) + string[i] = (char) toupper ((int)string[i]); +} + +/************************************************************************/ +/* DBFGetFieldIndex() */ +/* */ +/* Get the index number for a field in a .dbf file. */ +/* */ +/* Contributed by Jim Matthews. */ +/************************************************************************/ + +int SHPAPI_CALL +DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) + +{ + char name[12], name1[12], name2[12]; + int i; + + strncpy(name1, pszFieldName,11); + name1[11] = '\0'; + str_to_upper(name1); + + for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) + { + DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); + strncpy(name2,name,11); + str_to_upper(name2); + + if(!strncmp(name1,name2,10)) + return(i); + } + return(-1); +} + +/************************************************************************/ +/* DBFIsRecordDeleted() */ +/* */ +/* Returns TRUE if the indicated record is deleted, otherwise */ +/* it returns FALSE. */ +/************************************************************************/ + +int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ) + +{ +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( iShape < 0 || iShape >= psDBF->nRecords ) + return TRUE; + +/* -------------------------------------------------------------------- */ +/* Have we read the record? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, iShape ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* '*' means deleted. */ +/* -------------------------------------------------------------------- */ + return psDBF->pszCurrentRecord[0] == '*'; +} + +/************************************************************************/ +/* DBFMarkRecordDeleted() */ +/************************************************************************/ + +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, + int bIsDeleted ) + +{ + char chNewFlag; + +/* -------------------------------------------------------------------- */ +/* Verify selection. */ +/* -------------------------------------------------------------------- */ + if( iShape < 0 || iShape >= psDBF->nRecords ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Is this an existing record, but different than the last one */ +/* we accessed? */ +/* -------------------------------------------------------------------- */ + if( !DBFLoadRecord( psDBF, iShape ) ) + return FALSE; + +/* -------------------------------------------------------------------- */ +/* Assign value, marking record as dirty if it changes. */ +/* -------------------------------------------------------------------- */ + if( bIsDeleted ) + chNewFlag = '*'; + else + chNewFlag = ' '; + + if( psDBF->pszCurrentRecord[0] != chNewFlag ) + { + psDBF->bCurrentRecordModified = TRUE; + psDBF->bUpdated = TRUE; + psDBF->pszCurrentRecord[0] = chNewFlag; + } + + return TRUE; +} + +/************************************************************************/ +/* DBFGetCodePage */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +DBFGetCodePage(DBFHandle psDBF ) +{ + if( psDBF == NULL ) + return NULL; + return psDBF->pszCodePage; +} + +/************************************************************************/ +/* DBFDeleteField() */ +/* */ +/* Remove a field from a .dbf file */ +/************************************************************************/ + +int SHPAPI_CALL +DBFDeleteField(DBFHandle psDBF, int iField) +{ + int nOldRecordLength, nOldHeaderLength; + int nDeletedFieldOffset, nDeletedFieldSize; + SAOffset nRecordOffset; + char* pszRecord; + int i, iRecord; + + if (iField < 0 || iField >= psDBF->nFields) + return FALSE; + + /* make sure that everything is written in .dbf */ + if( !DBFFlushRecord( psDBF ) ) + return FALSE; + + /* get information about field to be deleted */ + nOldRecordLength = psDBF->nRecordLength; + nOldHeaderLength = psDBF->nHeaderLength; + nDeletedFieldOffset = psDBF->panFieldOffset[iField]; + nDeletedFieldSize = psDBF->panFieldSize[iField]; + + /* update fields info */ + for (i = iField + 1; i < psDBF->nFields; i++) + { + psDBF->panFieldOffset[i-1] = psDBF->panFieldOffset[i] - nDeletedFieldSize; + psDBF->panFieldSize[i-1] = psDBF->panFieldSize[i]; + psDBF->panFieldDecimals[i-1] = psDBF->panFieldDecimals[i]; + psDBF->pachFieldType[i-1] = psDBF->pachFieldType[i]; + } + + /* resize fields arrays */ + psDBF->nFields--; + + psDBF->panFieldOffset = (int *) + SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldSize = (int *) + SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); + + psDBF->panFieldDecimals = (int *) + SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); + + psDBF->pachFieldType = (char *) + SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); + + /* update header information */ + psDBF->nHeaderLength -= 32; + psDBF->nRecordLength -= nDeletedFieldSize; + + /* overwrite field information in header */ + memcpy(psDBF->pszHeader + iField*32, + psDBF->pszHeader + (iField+1)*32, + sizeof(char) * (psDBF->nFields - iField)*32); + + psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); + + /* update size of current record appropriately */ + psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, + psDBF->nRecordLength); + + /* we're done if we're dealing with not yet created .dbf */ + if ( psDBF->bNoHeader && psDBF->nRecords == 0 ) + return TRUE; + + /* force update of header with new header and record length */ + psDBF->bNoHeader = TRUE; + DBFUpdateHeader( psDBF ); + + /* alloc record */ + pszRecord = (char *) malloc(sizeof(char) * nOldRecordLength); + + /* shift records to their new positions */ + for (iRecord = 0; iRecord < psDBF->nRecords; iRecord++) + { + nRecordOffset = + nOldRecordLength * (SAOffset) iRecord + nOldHeaderLength; + + /* load record */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FRead( pszRecord, nOldRecordLength, 1, psDBF->fp ); + + nRecordOffset = + psDBF->nRecordLength * (SAOffset) iRecord + psDBF->nHeaderLength; + + /* move record in two steps */ + psDBF->sHooks.FSeek( psDBF->fp, nRecordOffset, 0 ); + psDBF->sHooks.FWrite( pszRecord, nDeletedFieldOffset, 1, psDBF->fp ); + psDBF->sHooks.FWrite( pszRecord + nDeletedFieldOffset + nDeletedFieldSize, + nOldRecordLength - nDeletedFieldOffset - nDeletedFieldSize, + 1, psDBF->fp ); + + } + + /* TODO: truncate file */ + + /* free record */ + free(pszRecord); + + return TRUE; +} diff --git a/map/shapefile/shapefil.h b/map/shapefile/shapefil.h new file mode 100644 index 00000000..059244b0 --- /dev/null +++ b/map/shapefile/shapefil.h @@ -0,0 +1,609 @@ +#ifndef SHAPEFILE_H_INCLUDED +#define SHAPEFILE_H_INCLUDED + +/****************************************************************************** + * $Id: shapefil.h 15715 2008-11-12 15:15:21Z warmerdam $ + * + * Project: Shapelib + * Purpose: Primary include file for Shapelib. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shapefil.h,v $ + * Revision 1.46 2008/11/12 14:28:15 fwarmerdam + * DBFCreateField() now works on files with records + * + * Revision 1.45 2008/11/11 17:47:10 fwarmerdam + * added DBFDeleteField() function + * + * Revision 1.44 2008/01/16 20:05:19 bram + * Add file hooks that accept UTF-8 encoded filenames on some platforms. Use SASetupUtf8Hooks + * tosetup the hooks and check SHPAPI_UTF8_HOOKS for its availability. Currently, this + * is only available on the Windows platform that decodes the UTF-8 filenames to wide + * character strings and feeds them to _wfopen and _wremove. + * + * Revision 1.43 2008/01/10 16:35:30 fwarmerdam + * avoid _ prefix on #defined symbols (bug 1840) + * + * Revision 1.42 2007/12/18 18:28:14 bram + * - create hook for client specific atof (bugzilla ticket 1615) + * - check for NULL handle before closing cpCPG file, and close after reading. + * + * Revision 1.41 2007/12/15 20:25:32 bram + * dbfopen.c now reads the Code Page information from the DBF file, and exports + * this information as a string through the DBFGetCodePage function. This is + * either the number from the LDID header field ("LDID/<number>") or as the + * content of an accompanying .CPG file. When creating a DBF file, the code can + * be set using DBFCreateEx. + * + * Revision 1.40 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.39 2007/12/04 20:37:56 fwarmerdam + * preliminary implementation of hooks api for io and errors + * + * Revision 1.38 2007/11/21 22:39:56 fwarmerdam + * close shx file in readonly mode (GDAL #1956) + * + * Revision 1.37 2007/10/27 03:31:14 fwarmerdam + * limit default depth of tree to 12 levels (gdal ticket #1594) + * + * Revision 1.36 2007/09/10 23:33:15 fwarmerdam + * Upstreamed support for visibility flag in SHPAPI_CALL for the needs + * of GDAL (gdal ticket #1810). + * + * Revision 1.35 2007/09/03 19:48:10 fwarmerdam + * move DBFReadAttribute() static dDoubleField into dbfinfo + * + * Revision 1.34 2006/06/17 15:33:32 fwarmerdam + * added pszWorkField - bug 1202 (rso) + * + * Revision 1.33 2006/02/15 01:14:30 fwarmerdam + * added DBFAddNativeFieldType + * + * Revision 1.32 2006/01/26 15:07:32 fwarmerdam + * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 + * + * Revision 1.31 2006/01/05 01:27:27 fwarmerdam + * added dbf deletion mark/fetch + * + * Revision 1.30 2005/01/03 22:30:13 fwarmerdam + * added support for saved quadtrees + * + * Revision 1.29 2004/09/26 20:09:35 fwarmerdam + * avoid rcsid warnings + * + * Revision 1.28 2003/12/29 06:02:18 fwarmerdam + * added cpl_error.h option + * + * Revision 1.27 2003/04/21 18:30:37 warmerda + * added header write/update public methods + * + * Revision 1.26 2002/09/29 00:00:08 warmerda + * added FTLogical and logical attribute read/write calls + * + * Revision 1.25 2002/05/07 13:46:30 warmerda + * added DBFWriteAttributeDirectly(). + * + * Revision 1.24 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.23 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.22 2002/01/15 14:32:00 warmerda + * try to improve SHPAPI_CALL docs + */ + +#include <stdio.h> + +#ifdef USE_DBMALLOC +#include <dbmalloc.h> +#endif + +#ifdef USE_CPL +#include "cpl_error.h" +#include "cpl_vsi.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/************************************************************************/ +/* Configuration options. */ +/************************************************************************/ + +/* -------------------------------------------------------------------- */ +/* Should the DBFReadStringAttribute() strip leading and */ +/* trailing white space? */ +/* -------------------------------------------------------------------- */ +#define TRIM_DBF_WHITESPACE + +/* -------------------------------------------------------------------- */ +/* Should we write measure values to the Multipatch object? */ +/* Reportedly ArcView crashes if we do write it, so for now it */ +/* is disabled. */ +/* -------------------------------------------------------------------- */ +#define DISABLE_MULTIPATCH_MEASURE + +/* -------------------------------------------------------------------- */ +/* SHPAPI_CALL */ +/* */ +/* The following two macros are present to allow forcing */ +/* various calling conventions on the Shapelib API. */ +/* */ +/* To force __stdcall conventions (needed to call Shapelib */ +/* from Visual Basic and/or Dephi I believe) the makefile could */ +/* be modified to define: */ +/* */ +/* /DSHPAPI_CALL=__stdcall */ +/* */ +/* If it is desired to force export of the Shapelib API without */ +/* using the shapelib.def file, use the following definition. */ +/* */ +/* /DSHAPELIB_DLLEXPORT */ +/* */ +/* To get both at once it will be necessary to hack this */ +/* include file to define: */ +/* */ +/* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ +/* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ +/* */ +/* The complexity of the situtation is partly caused by the */ +/* peculiar requirement of Visual C++ that __stdcall appear */ +/* after any "*"'s in the return value of a function while the */ +/* __declspec(dllexport) must appear before them. */ +/* -------------------------------------------------------------------- */ + +#ifdef SHAPELIB_DLLEXPORT +# define SHPAPI_CALL __declspec(dllexport) +# define SHPAPI_CALL1(x) __declspec(dllexport) x +#endif + +#ifndef SHPAPI_CALL +# if defined(USE_GCC_VISIBILITY_FLAG) +# define SHPAPI_CALL __attribute__ ((visibility("default"))) +# define SHPAPI_CALL1(x) __attribute__ ((visibility("default"))) x +# else +# define SHPAPI_CALL +# endif +#endif + +#ifndef SHPAPI_CALL1 +# define SHPAPI_CALL1(x) x SHPAPI_CALL +#endif + +/* -------------------------------------------------------------------- */ +/* Macros for controlling CVSID and ensuring they don't appear */ +/* as unreferenced variables resulting in lots of warnings. */ +/* -------------------------------------------------------------------- */ +#ifndef DISABLE_CVSID +# define SHP_CVSID(string) static char cpl_cvsid[] = string; \ +static char *cvsid_aw() { return( cvsid_aw() ? ((char *) NULL) : cpl_cvsid ); } +#else +# define SHP_CVSID(string) +#endif + +/* -------------------------------------------------------------------- */ +/* On some platforms, additional file IO hooks are defined that */ +/* UTF-8 encoded filenames Unicode filenames */ +/* -------------------------------------------------------------------- */ +#if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define SHPAPI_WINDOWS +# define SHPAPI_UTF8_HOOKS +#endif + +/* -------------------------------------------------------------------- */ +/* IO/Error hook functions. */ +/* -------------------------------------------------------------------- */ +typedef int *SAFile; + +#ifndef SAOffset +typedef unsigned long SAOffset; +#endif + +typedef struct { + SAFile (*FOpen) ( const char *filename, const char *access); + SAOffset (*FRead) ( void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FWrite)( void *p, SAOffset size, SAOffset nmemb, SAFile file); + SAOffset (*FSeek) ( SAFile file, SAOffset offset, int whence ); + SAOffset (*FTell) ( SAFile file ); + int (*FFlush)( SAFile file ); + int (*FClose)( SAFile file ); + int (*Remove) ( const char *filename ); + + void (*Error) ( const char *message ); + double (*Atof) ( const char *str ); +} SAHooks; + +void SHPAPI_CALL SASetupDefaultHooks( SAHooks *psHooks ); +#ifdef SHPAPI_UTF8_HOOKS +void SHPAPI_CALL SASetupUtf8Hooks( SAHooks *psHooks ); +#endif + +/************************************************************************/ +/* SHP Support. */ +/************************************************************************/ +typedef struct +{ + SAHooks sHooks; + + SAFile fpSHP; + SAFile fpSHX; + + int nShapeType; /* SHPT_* */ + + int nFileSize; /* SHP file */ + + int nRecords; + int nMaxRecords; + int *panRecOffset; + int *panRecSize; + + double adBoundsMin[4]; + double adBoundsMax[4]; + + int bUpdated; + + unsigned char *pabyRec; + int nBufSize; +} SHPInfo; + +typedef SHPInfo * SHPHandle; + +/* -------------------------------------------------------------------- */ +/* Shape types (nSHPType) */ +/* -------------------------------------------------------------------- */ +#define SHPT_NULL 0 +#define SHPT_POINT 1 +#define SHPT_ARC 3 +#define SHPT_POLYGON 5 +#define SHPT_MULTIPOINT 8 +#define SHPT_POINTZ 11 +#define SHPT_ARCZ 13 +#define SHPT_POLYGONZ 15 +#define SHPT_MULTIPOINTZ 18 +#define SHPT_POINTM 21 +#define SHPT_ARCM 23 +#define SHPT_POLYGONM 25 +#define SHPT_MULTIPOINTM 28 +#define SHPT_MULTIPATCH 31 + + +/* -------------------------------------------------------------------- */ +/* Part types - everything but SHPT_MULTIPATCH just uses */ +/* SHPP_RING. */ +/* -------------------------------------------------------------------- */ + +#define SHPP_TRISTRIP 0 +#define SHPP_TRIFAN 1 +#define SHPP_OUTERRING 2 +#define SHPP_INNERRING 3 +#define SHPP_FIRSTRING 4 +#define SHPP_RING 5 + +/* -------------------------------------------------------------------- */ +/* SHPObject - represents on shape (without attributes) read */ +/* from the .shp file. */ +/* -------------------------------------------------------------------- */ +typedef struct +{ + int nSHPType; + + int nShapeId; /* -1 is unknown/unassigned */ + + int nParts; + int *panPartStart; + int *panPartType; + + int nVertices; + double *padfX; + double *padfY; + double *padfZ; + double *padfM; + + double dfXMin; + double dfYMin; + double dfZMin; + double dfMMin; + + double dfXMax; + double dfYMax; + double dfZMax; + double dfMMax; + + int bMeasureIsUsed; +} SHPObject; + +/* -------------------------------------------------------------------- */ +/* SHP API Prototypes */ +/* -------------------------------------------------------------------- */ + +/* If pszAccess is read-only, the fpSHX field of the returned structure */ +/* will be NULL as it is not necessary to keep the SHX file open */ +SHPHandle SHPAPI_CALL + SHPOpen( const char * pszShapeFile, const char * pszAccess ); +SHPHandle SHPAPI_CALL + SHPOpenLL( const char *pszShapeFile, const char *pszAccess, + SAHooks *psHooks ); +SHPHandle SHPAPI_CALL + SHPCreate( const char * pszShapeFile, int nShapeType ); +SHPHandle SHPAPI_CALL + SHPCreateLL( const char * pszShapeFile, int nShapeType, + SAHooks *psHooks ); +void SHPAPI_CALL + SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ); + +SHPObject SHPAPI_CALL1(*) + SHPReadObject( SHPHandle hSHP, int iShape ); +int SHPAPI_CALL + SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject ); + +void SHPAPI_CALL + SHPDestroyObject( SHPObject * psObject ); +void SHPAPI_CALL + SHPComputeExtents( SHPObject * psObject ); +SHPObject SHPAPI_CALL1(*) + SHPCreateObject( int nSHPType, int nShapeId, int nParts, + const int * panPartStart, const int * panPartType, + int nVertices, + const double * padfX, const double * padfY, + const double * padfZ, const double * padfM ); +SHPObject SHPAPI_CALL1(*) + SHPCreateSimpleObject( int nSHPType, int nVertices, + const double * padfX, + const double * padfY, + const double * padfZ ); + +int SHPAPI_CALL + SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ); + +void SHPAPI_CALL SHPClose( SHPHandle hSHP ); +void SHPAPI_CALL SHPWriteHeader( SHPHandle hSHP ); + +const char SHPAPI_CALL1(*) + SHPTypeName( int nSHPType ); +const char SHPAPI_CALL1(*) + SHPPartTypeName( int nPartType ); + +/* -------------------------------------------------------------------- */ +/* Shape quadtree indexing API. */ +/* -------------------------------------------------------------------- */ + +/* this can be two or four for binary or quad tree */ +#define MAX_SUBNODE 4 + +/* upper limit of tree levels for automatic estimation */ +#define MAX_DEFAULT_TREE_DEPTH 12 + +typedef struct shape_tree_node +{ + /* region covered by this node */ + double adfBoundsMin[4]; + double adfBoundsMax[4]; + + /* list of shapes stored at this node. The papsShapeObj pointers + or the whole list can be NULL */ + int nShapeCount; + int *panShapeIds; + SHPObject **papsShapeObj; + + int nSubNodes; + struct shape_tree_node *apsSubNode[MAX_SUBNODE]; + +} SHPTreeNode; + +typedef struct +{ + SHPHandle hSHP; + + int nMaxDepth; + int nDimension; + int nTotalCount; + + SHPTreeNode *psRoot; +} SHPTree; + +SHPTree SHPAPI_CALL1(*) + SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ); +void SHPAPI_CALL + SHPDestroyTree( SHPTree * hTree ); + +int SHPAPI_CALL + SHPWriteTree( SHPTree *hTree, const char * pszFilename ); +SHPTree SHPAPI_CALL + SHPReadTree( const char * pszFilename ); + +int SHPAPI_CALL + SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); +int SHPAPI_CALL + SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); + +void SHPAPI_CALL + SHPTreeTrimExtraNodes( SHPTree * hTree ); + +int SHPAPI_CALL1(*) + SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, + double * padfBoundsMax, + int * ); +int SHPAPI_CALL + SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); + +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ); + +/************************************************************************/ +/* DBF Support. */ +/************************************************************************/ +typedef struct +{ + SAHooks sHooks; + + SAFile fp; + + int nRecords; + + int nRecordLength; + int nHeaderLength; + int nFields; + int *panFieldOffset; + int *panFieldSize; + int *panFieldDecimals; + char *pachFieldType; + + char *pszHeader; + + int nCurrentRecord; + int bCurrentRecordModified; + char *pszCurrentRecord; + + int nWorkFieldLength; + char *pszWorkField; + + int bNoHeader; + int bUpdated; + + double dfDoubleField; + + int iLanguageDriver; + char *pszCodePage; +} DBFInfo; + +typedef DBFInfo * DBFHandle; + +typedef enum { + FTString, + FTInteger, + FTDouble, + FTLogical, + FTInvalid +} DBFFieldType; + +#define XBASE_FLDHDR_SZ 32 + + +DBFHandle SHPAPI_CALL + DBFOpen( const char * pszDBFFile, const char * pszAccess ); +DBFHandle SHPAPI_CALL + DBFOpenLL( const char * pszDBFFile, const char * pszAccess, + SAHooks *psHooks ); +DBFHandle SHPAPI_CALL + DBFCreate( const char * pszDBFFile ); +DBFHandle SHPAPI_CALL + DBFCreateEx( const char * pszDBFFile, const char * pszCodePage ); +DBFHandle SHPAPI_CALL + DBFCreateLL( const char * pszDBFFile, const char * pszCodePage, SAHooks *psHooks ); + +int SHPAPI_CALL + DBFGetFieldCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFGetRecordCount( DBFHandle psDBF ); +int SHPAPI_CALL + DBFAddField( DBFHandle hDBF, const char * pszFieldName, + DBFFieldType eType, int nWidth, int nDecimals ); + +int SHPAPI_CALL + DBFAddNativeFieldType( DBFHandle hDBF, const char * pszFieldName, + char chType, int nWidth, int nDecimals ); + +int SHPAPI_CALL + DBFDeleteField( DBFHandle hDBF, int iField ); + +DBFFieldType SHPAPI_CALL + DBFGetFieldInfo( DBFHandle psDBF, int iField, + char * pszFieldName, int * pnWidth, int * pnDecimals ); + +int SHPAPI_CALL + DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); + +int SHPAPI_CALL + DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); +double SHPAPI_CALL + DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); +const char SHPAPI_CALL1(*) + DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); +int SHPAPI_CALL + DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, + int nFieldValue ); +int SHPAPI_CALL + DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, + double dFieldValue ); +int SHPAPI_CALL + DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, + const char * pszFieldValue ); +int SHPAPI_CALL + DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField ); + +int SHPAPI_CALL + DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, + const char lFieldValue); +int SHPAPI_CALL + DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, + void * pValue ); +const char SHPAPI_CALL1(*) + DBFReadTuple(DBFHandle psDBF, int hEntity ); +int SHPAPI_CALL + DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); + +int SHPAPI_CALL DBFIsRecordDeleted( DBFHandle psDBF, int iShape ); +int SHPAPI_CALL DBFMarkRecordDeleted( DBFHandle psDBF, int iShape, + int bIsDeleted ); + +DBFHandle SHPAPI_CALL + DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); + +void SHPAPI_CALL + DBFClose( DBFHandle hDBF ); +void SHPAPI_CALL + DBFUpdateHeader( DBFHandle hDBF ); +char SHPAPI_CALL + DBFGetNativeFieldType( DBFHandle hDBF, int iField ); + +const char SHPAPI_CALL1(*) + DBFGetCodePage(DBFHandle psDBF ); + +#ifdef __cplusplus +} +#endif + +#endif /* ndef SHAPEFILE_H_INCLUDED */ diff --git a/map/shapefile/shapefile.c b/map/shapefile/shapefile.c new file mode 100644 index 00000000..41cc37b5 --- /dev/null +++ b/map/shapefile/shapefile.c @@ -0,0 +1,748 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <unistd.h> +#include "config.h" +#include "debug.h" +#include "plugin.h" +#include "projection.h" +#include "item.h" +#include "map.h" +#include "maptype.h" +#include "attr.h" +#include "transform.h" +#include "file.h" +#include "shapefil.h" + + +struct map_priv { + int id; + char *filename; + char *charset; + SHPHandle hSHP; + DBFHandle hDBF; + int nShapeType, nEntities, nFields; + double adfMinBound[4], adfMaxBound[4]; + struct longest_match *lm; + char *dbfmap_data; + struct coord offset; + enum projection pro; +}; + + +struct map_rect_priv { + struct map_selection *sel; + struct map_priv *m; + struct item item; + int idx; + int cidx; + int aidx; + enum attr_type anext; + SHPObject *psShape; + char *str; + char *line; + int attr_pos; + struct attr *attr; +}; + +static void +map_destroy_shapefile(struct map_priv *m) +{ + dbg(1,"map_destroy_shapefile\n"); + g_free(m); +} + +static void +shapefile_coord_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + mr->cidx=0; +} + +static int +shapefile_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + struct coord cs; + int ret=0; + int idx; + + struct coord_geo g; + SHPObject *psShape=mr->psShape; + while (count) { + idx=mr->cidx; + if (idx >= psShape->nVertices) + break; + cs.x=psShape->padfX[idx]+mr->m->offset.x; + cs.y=psShape->padfY[idx]+mr->m->offset.y; + if (!mr->m->pro) { + g.lng=cs.x; + g.lat=cs.y; + transform_from_geo(projection_mg, &g, c); + } else + transform_from_to(&cs, mr->m->pro, c, projection_mg); + mr->cidx++; + ret++; + c++; + count--; + } + return ret; +} + +static void +shapefile_attr_rewind(void *priv_data) +{ + struct map_rect_priv *mr=priv_data; + mr->aidx=0; + mr->attr_pos=0; + mr->anext=attr_debug; +} + + +struct longest_match_list_item { + void *data; + int match_idx_count; + int *match_idx; +}; + +struct longest_match_list { + GList *longest_match_list_items; +}; + + +struct longest_match { + GHashTable *match_hash; + char *match_present; + int match_present_count; + GList *longest_match_lists; + int longest_match_list_count; +}; + +static void +longest_match_add_match(struct longest_match *lm, struct longest_match_list_item *lmi, char *match) +{ + int idx; + if (!(idx=(int)(long)g_hash_table_lookup(lm->match_hash, match))) { + idx=lm->match_present_count++; + lm->match_present=g_renew(char, lm->match_present, lm->match_present_count); + g_hash_table_insert(lm->match_hash, g_strdup(match), (gpointer)(long)idx); + } + lmi->match_idx=g_renew(int, lmi->match_idx, lmi->match_idx_count+1); + lmi->match_idx[lmi->match_idx_count++]=idx; +} + +static void +longest_match_item_destroy(struct longest_match_list_item *lmi, long flags) +{ + if (!lmi) + return; + if (flags & 2) { + g_free(lmi->data); + } + g_free(lmi->match_idx); + g_free(lmi); +} + +static struct longest_match_list_item * +longest_match_item_new(struct longest_match_list *lml, void *data) +{ + struct longest_match_list_item *ret=g_new0(struct longest_match_list_item,1); + lml->longest_match_list_items=g_list_append(lml->longest_match_list_items, ret); + ret->data=data; + + return ret; +} + +static struct longest_match_list * +longest_match_list_new(struct longest_match *lm) +{ + struct longest_match_list *ret=g_new0(struct longest_match_list,1); + lm->longest_match_lists=g_list_append(lm->longest_match_lists, ret); + return ret; +} + +static void +longest_match_list_destroy(struct longest_match_list *lml, long flags) +{ + if (!lml) + return; + if (flags & 1) { + g_list_foreach(lml->longest_match_list_items, (GFunc)longest_match_item_destroy, (gpointer)flags); + g_list_free(lml->longest_match_list_items); + } + g_free(lml); +} + +static struct longest_match_list * +longest_match_get_list(struct longest_match *lm, int list) +{ + GList *l=lm->longest_match_lists; + while (l && list > 0) { + l=g_list_next(l); + list++; + } + if (l) + return l->data; + return NULL; +} + +static struct longest_match * +longest_match_new(void) +{ + struct longest_match *ret=g_new0(struct longest_match,1); + ret->match_hash=g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + ret->match_present_count=1; + return ret; +} + +static void +longest_match_destroy(struct longest_match *lm, long flags) +{ + if (!lm) + return; + if (flags & 1) { + g_list_foreach(lm->longest_match_lists, (GFunc)longest_match_list_destroy, (gpointer)flags); + g_list_free(lm->longest_match_lists); + } + g_hash_table_destroy(lm->match_hash); + g_free(lm->match_present); + g_free(lm); +} + +static void +longest_match_clear(struct longest_match *lm) +{ + if (lm->match_present) + memset(lm->match_present, 0, lm->match_present_count); +} + +static void +longest_match_add_key_value(struct longest_match *lm, char *k, char *v) +{ + char buffer[strlen(k)+strlen(v)+2]; + int idx; + + strcpy(buffer,"*=*"); + if ((idx=(int)(long)g_hash_table_lookup(lm->match_hash, buffer))) + lm->match_present[idx]=1; + + sprintf(buffer,"%s=*", k); + if ((idx=(int)(long)g_hash_table_lookup(lm->match_hash, buffer))) + lm->match_present[idx]=2; + + sprintf(buffer,"*=%s", v); + if ((idx=(int)(long)g_hash_table_lookup(lm->match_hash, buffer))) + lm->match_present[idx]=2; + + sprintf(buffer,"%s=%s", k, v); + if ((idx=(int)(long)g_hash_table_lookup(lm->match_hash, buffer))) + lm->match_present[idx]=4; +} + +static int +longest_match_list_find(struct longest_match *lm, struct longest_match_list *lml, void **list, int max_list_len) +{ + int j,longest=0,ret=0,sum,val; + struct longest_match_list_item *curr; + GList *l=lml->longest_match_list_items; + + + while (l) { + sum=0; + curr=l->data; + for (j = 0 ; j < curr->match_idx_count ; j++) { + val=lm->match_present[curr->match_idx[j]]; + if (val) + sum+=val; + else { + sum=-1; + break; + } + } + if (sum > longest) { + longest=sum; + ret=0; + } + if (sum > 0 && sum == longest && ret < max_list_len) + list[ret++]=curr->data; + l=g_list_next(l); + } + return ret; +} + + +static void +build_match(struct longest_match *lm, struct longest_match_list *lml, char *line) +{ + char *kvl=NULL,*i=NULL,*p,*kv; + dbg(1,"line=%s\n",line); + struct longest_match_list_item *lmli; + kvl=line; + p=strchr(line,'\t'); + if (p) { + while (*p == '\t') + *p++='\0'; + i=p; + } + lmli=longest_match_item_new(lml,g_strdup(i)); + while ((kv=strtok(kvl, ","))) { + kvl=NULL; + longest_match_add_match(lm, lmli, kv); + } +} + +static void +build_matches(struct map_priv *m,char *matches) +{ + char *matches2=g_strdup(matches); + char *l=matches2,*p; + struct longest_match_list *lml; + + m->lm=longest_match_new(); + lml=longest_match_list_new(m->lm); + while (l) { + p=strchr(l,'\n'); + if (p) + *p++='\0'; + if (strlen(l)) + build_match(m->lm,lml,l); + l=p; + } + g_free(matches2); +} + +static void +process_fields(struct map_priv *m, int id) +{ + int i; + char szTitle[12],*pszTypeName,*str; + int nWidth, nDecimals; + + for (i = 0 ; i < m->nFields ; i++) { + + switch (DBFGetFieldInfo(m->hDBF, i, szTitle, &nWidth, &nDecimals )) { + case FTString: + pszTypeName = "String"; + str=g_strdup(DBFReadStringAttribute( m->hDBF, id, i )); + break; + case FTInteger: + pszTypeName = "Integer"; + str=g_strdup_printf("%d",DBFReadIntegerAttribute( m->hDBF, id, i )); + break; + case FTDouble: + pszTypeName = "Double"; + str=g_strdup_printf("%lf",DBFReadDoubleAttribute( m->hDBF, id, i )); + break; + case FTInvalid: + pszTypeName = "Invalid"; + str=NULL; + break; + default: + pszTypeName = "Unknown"; + str=NULL; + } + longest_match_add_key_value(m->lm, szTitle, str); + } +} + +static int +attr_resolve(struct map_rect_priv *mr, enum attr_type attr_type, struct attr *attr) +{ + char name[1024]; + char value[1024]; + char szTitle[12]; + const char *str; + char *col,*type=NULL; + int i,len, nWidth, nDecimals; + if (!mr->line) + return 0; + if (attr_type != attr_any) + type=attr_to_name(attr_type); + if (attr_from_line(mr->line,type,&mr->attr_pos,value,name)) { + len=strlen(value); + if (value[0] == '$' && value[1] == '{' && value[len-1] == '}') { + value[len-1]='\0'; + col=value+2; + for (i = 0 ; i < mr->m->nFields ; i++) { + if (DBFGetFieldInfo(mr->m->hDBF, i, szTitle, &nWidth, &nDecimals ) == FTString && !strcmp(szTitle,col)) { + str=DBFReadStringAttribute( mr->m->hDBF, mr->item.id_lo, i); + strcpy(value,str); + break; + } + } + } + if (!value[0]) + return -1; + dbg(1,"name=%s value=%s\n",name,value); + attr_free(mr->attr); + mr->attr=attr_new_from_text(name,value); + if (mr->attr) { + *attr=*mr->attr; + return 1; + } + return -1; + } + return 0; +} + +static int +shapefile_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + struct map_priv *m=mr->m; + char szTitle[12],*pszTypeName, *str; + int code, ret, nWidth, nDecimals; + + attr->type=attr_type; + switch (attr_type) { + case attr_any: + while ((code=attr_resolve(mr, attr_type, attr))) { + if (code == 1) + return 1; + } + while (mr->anext != attr_none) { + ret=shapefile_attr_get(priv_data, mr->anext, attr); + if (ret) + return ret; + } + return 0; + case attr_debug: + if (mr->aidx >= m->nFields) { + mr->anext=attr_none; + return 0; + } + switch (DBFGetFieldInfo(m->hDBF, mr->aidx, szTitle, &nWidth, &nDecimals )) { + case FTString: + pszTypeName = "String"; + str=g_strdup(DBFReadStringAttribute( m->hDBF, mr->item.id_lo, mr->aidx )); + break; + case FTInteger: + pszTypeName = "Integer"; + str=g_strdup_printf("%d",DBFReadIntegerAttribute( m->hDBF, mr->item.id_lo, mr->aidx )); + break; + case FTDouble: + pszTypeName = "Double"; + str=g_strdup_printf("%lf",DBFReadDoubleAttribute( m->hDBF, mr->item.id_lo, mr->aidx )); + break; + case FTInvalid: + pszTypeName = "Invalid"; + str=NULL; + break; + default: + pszTypeName = "Unknown"; + str=NULL; + } + g_free(mr->str); + mr->str=attr->u.str=g_strdup_printf("%s=%s(%s)",szTitle,str,pszTypeName); + g_free(str); + mr->aidx++; + return 1; + default: + return attr_resolve(mr, attr_type, attr); + } +} + +static struct item_methods methods_shapefile = { + shapefile_coord_rewind, + shapefile_coord_get, + shapefile_attr_rewind, + shapefile_attr_get, +}; + +static struct map_rect_priv * +map_rect_new_shapefile(struct map_priv *map, struct map_selection *sel) +{ + struct map_rect_priv *mr; + char *dbfmapfile=g_strdup_printf("%s.dbfmap", map->filename); + void *data; + struct file *file; + int size; + int changed=0; + if ((file=file_create(dbfmapfile))) { + size=file_size(file); + data=file_data_read_all(file); + if (data) { + if (!map->dbfmap_data || size != strlen(map->dbfmap_data) || strncmp(data,map->dbfmap_data,size)) { + g_free(map->dbfmap_data); + map->dbfmap_data=g_malloc(size+1); + memcpy(map->dbfmap_data, data, size); + map->dbfmap_data[size]='\0'; + changed=1; + } + file_data_free(file, data); + } + file_destroy(file); + } else { + if (map->dbfmap_data) { + changed=1; + g_free(map->dbfmap_data); + map->dbfmap_data=NULL; + } + } + dbg(1,"%s changed %d old %p\n",dbfmapfile,changed,map->dbfmap_data); + if (changed) { + longest_match_destroy(map->lm,1); + map->lm=NULL; + if (map->dbfmap_data) { + build_matches(map,map->dbfmap_data); + } + } + dbg(1,"map_rect_new_shapefile\n"); + mr=g_new0(struct map_rect_priv, 1); + mr->m=map; + mr->idx=0; + mr->item.id_lo=0; + mr->item.id_hi=0; + mr->item.meth=&methods_shapefile; + mr->item.priv_data=mr; + g_free(dbfmapfile); + return mr; +} + + +static void +map_rect_destroy_shapefile(struct map_rect_priv *mr) +{ + if (mr->psShape) + SHPDestroyObject(mr->psShape); + attr_free(mr->attr); + g_free(mr->str); + g_free(mr); +} + +static struct item * +map_rect_get_item_shapefile(struct map_rect_priv *mr) +{ + struct map_priv *m=mr->m; + void *lines[5]; + struct longest_match_list *lml; + int count; + char type[1024]; + + if (mr->idx >= m->nEntities) + return NULL; + mr->item.id_lo=mr->idx; + if (mr->psShape) + SHPDestroyObject(mr->psShape); + mr->psShape=SHPReadObject(m->hSHP, mr->idx); + if (mr->psShape->nVertices > 1) + mr->item.type=type_street_unkn; + else + mr->item.type=type_point_unkn; + if (m->lm) { + longest_match_clear(m->lm); + process_fields(m, mr->idx); + + lml=longest_match_get_list(m->lm, 0); + count=longest_match_list_find(m->lm, lml, lines, sizeof(lines)/sizeof(void *)); + if (count) { + mr->line=lines[0]; + if (attr_from_line(mr->line,"type",NULL,type,NULL)) { + dbg(1,"type='%s'\n", type); + mr->item.type=item_from_name(type); + if (mr->item.type == type_none) + dbg(0,"Warning: type '%s' unknown\n", type); + } + } else + mr->line=NULL; + } + mr->idx++; + shapefile_coord_rewind(mr); + shapefile_attr_rewind(mr); + return &mr->item; +} + +static struct item * +map_rect_get_item_byid_shapefile(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + mr->idx=id_lo; + return map_rect_get_item_shapefile(mr); +} + +static struct map_methods map_methods_shapefile = { + projection_mg, + "iso8859-1", + map_destroy_shapefile, + map_rect_new_shapefile, + map_rect_destroy_shapefile, + map_rect_get_item_shapefile, + map_rect_get_item_byid_shapefile, +}; + +static struct map_priv * +map_new_shapefile(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + struct attr *data=attr_search(attrs, NULL, attr_data); + struct attr *charset=attr_search(attrs, NULL, attr_charset); + struct attr *projectionname=attr_search(attrs, NULL, attr_projectionname); + struct file_wordexp *wexp; + char *wdata; + char **wexp_data; + char *shapefile,*dbffile; + if (! data) + return NULL; + dbg(1,"map_new_shapefile %s\n", data->u.str); + wdata=g_strdup(data->u.str); + wexp=file_wordexp_new(wdata); + wexp_data=file_wordexp_get_array(wexp); + *meth=map_methods_shapefile; + + m=g_new0(struct map_priv, 1); + m->filename=g_strdup(wexp_data[0]); + shapefile=g_strdup_printf("%s.shp", m->filename); + m->hSHP=SHPOpen(shapefile, "rb" ); + SHPGetInfo( m->hSHP, &m->nEntities, &m->nShapeType, m->adfMinBound, m->adfMaxBound ); + g_free(shapefile); + dbffile=g_strdup_printf("%s.dbf", m->filename); + m->hDBF=DBFOpen(dbffile, "rb"); + m->nFields=DBFGetFieldCount(m->hDBF); + g_free(dbffile); + dbg(1,"map_new_shapefile %s %s\n", m->filename, wdata); + if (charset) { + m->charset=g_strdup(charset->u.str); + meth->charset=m->charset; + } + if (projectionname) + m->pro=projection_from_name(projectionname->u.str, &m->offset); + file_wordexp_destroy(wexp); + return m; +} + +void +plugin_init(void) +{ + dbg(1,"shapefile: plugin_init\n"); + plugin_register_map_type("shapefile", map_new_shapefile); +} + +/************************************************************************/ +/* VSI_SHP_Open() */ +/************************************************************************/ + +static SAFile VSI_SHP_Open( const char *pszFilename, const char *pszAccess ) + +{ + return (SAFile)fopen(pszFilename, pszAccess); +} + +/************************************************************************/ +/* VSI_SHP_Read() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Read( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + return fread(p, size, nmemb, (FILE *)file); +} + +/************************************************************************/ +/* VSI_SHP_Write() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Write( void *p, SAOffset size, SAOffset nmemb, SAFile file ) + +{ + return fwrite(p, size, nmemb, (FILE *)file); +} + +/************************************************************************/ +/* VSI_SHP_Seek() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Seek( SAFile file, SAOffset offset, int whence ) + +{ + return fseek((FILE *)file, offset, whence); +} + +/************************************************************************/ +/* VSI_SHP_Tell() */ +/************************************************************************/ + +static SAOffset VSI_SHP_Tell( SAFile file ) + +{ + return ftell((FILE *)file); +} + + +static int VSI_SHP_Flush( SAFile file ) + +{ + return fflush((FILE *)file); +} + +/************************************************************************/ +/* VSI_SHP_Close() */ +/************************************************************************/ + +static int VSI_SHP_Close( SAFile file ) + +{ + return fclose((FILE *)file); +} + +/************************************************************************/ +/* SADError() */ +/************************************************************************/ + +static void VSI_SHP_Error( const char *message ) + +{ + dbg(0,"error:%s\n", message); +} + +/************************************************************************/ +/* VSI_SHP_Remove() */ +/************************************************************************/ + +static int VSI_SHP_Remove( const char *pszFilename ) + +{ + return unlink(pszFilename); +} + +/************************************************************************/ +/* SASetupDefaultHooks() */ +/************************************************************************/ + +void SASetupDefaultHooks( SAHooks *psHooks ) + +{ + psHooks->FOpen = VSI_SHP_Open; + psHooks->FRead = VSI_SHP_Read; + psHooks->FWrite = VSI_SHP_Write; + psHooks->FSeek = VSI_SHP_Seek; + psHooks->FTell = VSI_SHP_Tell; + psHooks->FFlush = VSI_SHP_Flush; + psHooks->FClose = VSI_SHP_Close; + + psHooks->Remove = VSI_SHP_Remove; + psHooks->Atof = atof; + + psHooks->Error = VSI_SHP_Error; +} + + diff --git a/map/shapefile/shpopen.c b/map/shapefile/shpopen.c new file mode 100644 index 00000000..ba0ae849 --- /dev/null +++ b/map/shapefile/shpopen.c @@ -0,0 +1,2284 @@ +/****************************************************************************** + * $Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of core Shapefile read/write functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, 2001, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shpopen.c,v $ + * Revision 1.59 2008/03/14 05:25:31 fwarmerdam + * Correct crash on buggy geometries (gdal #2218) + * + * Revision 1.58 2008/01/08 23:28:26 bram + * on line 2095, use a float instead of a double to avoid a compiler warning + * + * Revision 1.57 2007/12/06 07:00:25 fwarmerdam + * dbfopen now using SAHooks for fileio + * + * Revision 1.56 2007/12/04 20:37:56 fwarmerdam + * preliminary implementation of hooks api for io and errors + * + * Revision 1.55 2007/11/21 22:39:56 fwarmerdam + * close shx file in readonly mode (GDAL #1956) + * + * Revision 1.54 2007/11/15 00:12:47 mloskot + * Backported recent changes from GDAL (Ticket #1415) to Shapelib. + * + * Revision 1.53 2007/11/14 22:31:08 fwarmerdam + * checks after mallocs to detect for corrupted/voluntary broken shapefiles. + * http://trac.osgeo.org/gdal/ticket/1991 + * + * Revision 1.52 2007/06/21 15:58:33 fwarmerdam + * fix for SHPRewindObject when rings touch at one vertex (gdal #976) + * + * Revision 1.51 2006/09/04 15:24:01 fwarmerdam + * Fixed up log message for 1.49. + * + * Revision 1.50 2006/09/04 15:21:39 fwarmerdam + * fix of last fix + * + * Revision 1.49 2006/09/04 15:21:00 fwarmerdam + * MLoskot: Added stronger test of Shapefile reading failures, e.g. truncated + * files. The problem was discovered by Tim Sutton and reported here + * https://svn.qgis.org/trac/ticket/200 + * + * Revision 1.48 2006/01/26 15:07:32 fwarmerdam + * add bMeasureIsUsed flag from Craig Bruce: Bug 1249 + * + * Revision 1.47 2006/01/04 20:07:23 fwarmerdam + * In SHPWriteObject() make sure that the record length is updated + * when rewriting an existing record. + * + * Revision 1.46 2005/02/11 17:17:46 fwarmerdam + * added panPartStart[0] validation + * + * Revision 1.45 2004/09/26 20:09:48 fwarmerdam + * const correctness changes + * + * Revision 1.44 2003/12/29 00:18:39 fwarmerdam + * added error checking for failed IO and optional CPL error reporting + * + * Revision 1.43 2003/12/01 16:20:08 warmerda + * be careful of zero vertex shapes + * + * Revision 1.42 2003/12/01 14:58:27 warmerda + * added degenerate object check in SHPRewindObject() + * + * Revision 1.41 2003/07/08 15:22:43 warmerda + * avoid warning + * + * Revision 1.40 2003/04/21 18:30:37 warmerda + * added header write/update public methods + * + * Revision 1.39 2002/08/26 06:46:56 warmerda + * avoid c++ comments + * + * Revision 1.38 2002/05/07 16:43:39 warmerda + * Removed debugging printf. + * + * Revision 1.37 2002/04/10 17:35:22 warmerda + * fixed bug in ring reversal code + * + * Revision 1.36 2002/04/10 16:59:54 warmerda + * added SHPRewindObject + * + * Revision 1.35 2001/12/07 15:10:44 warmerda + * fix if .shx fails to open + * + * Revision 1.34 2001/11/01 16:29:55 warmerda + * move pabyRec into SHPInfo for thread safety + * + * Revision 1.33 2001/07/03 12:18:15 warmerda + * Improved cleanup if SHX not found, provied by Riccardo Cohen. + * + * Revision 1.32 2001/06/22 01:58:07 warmerda + * be more careful about establishing initial bounds in face of NULL shapes + * + * Revision 1.31 2001/05/31 19:35:29 warmerda + * added support for writing null shapes + * + * Revision 1.30 2001/05/28 12:46:29 warmerda + * Add some checking on reasonableness of record count when opening. + * + * Revision 1.29 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.28 2001/02/06 22:25:06 warmerda + * fixed memory leaks when SHPOpen() fails + * + * Revision 1.27 2000/07/18 15:21:33 warmerda + * added better enforcement of -1 for append in SHPWriteObject + * + * Revision 1.26 2000/02/16 16:03:51 warmerda + * added null shape support + * + * Revision 1.25 1999/12/15 13:47:07 warmerda + * Fixed record size settings in .shp file (was 4 words too long) + * Added stdlib.h. + * + * Revision 1.24 1999/11/05 14:12:04 warmerda + * updated license terms + * + * Revision 1.23 1999/07/27 00:53:46 warmerda + * added support for rewriting shapes + * + * Revision 1.22 1999/06/11 19:19:11 warmerda + * Cleanup pabyRec static buffer on SHPClose(). + * + * Revision 1.21 1999/06/02 14:57:56 kshih + * Remove unused variables + * + * Revision 1.20 1999/04/19 21:04:17 warmerda + * Fixed syntax error. + * + * Revision 1.19 1999/04/19 21:01:57 warmerda + * Force access string to binary in SHPOpen(). + * + * Revision 1.18 1999/04/01 18:48:07 warmerda + * Try upper case extensions if lower case doesn't work. + * + * Revision 1.17 1998/12/31 15:29:39 warmerda + * Disable writing measure values to multipatch objects if + * DISABLE_MULTIPATCH_MEASURE is defined. + * + * Revision 1.16 1998/12/16 05:14:33 warmerda + * Added support to write MULTIPATCH. Fixed reading Z coordinate of + * MULTIPATCH. Fixed record size written for all feature types. + * + * Revision 1.15 1998/12/03 16:35:29 warmerda + * r+b is proper binary access string, not rb+. + * + * Revision 1.14 1998/12/03 15:47:56 warmerda + * Fixed setting of nVertices in SHPCreateObject(). + * + * Revision 1.13 1998/12/03 15:33:54 warmerda + * Made SHPCalculateExtents() separately callable. + * + * Revision 1.12 1998/11/11 20:01:50 warmerda + * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. + * + * Revision 1.11 1998/11/09 20:56:44 warmerda + * Fixed up handling of file wide bounds. + * + * Revision 1.10 1998/11/09 20:18:51 warmerda + * Converted to support 3D shapefiles, and use of SHPObject. + * + * Revision 1.9 1998/02/24 15:09:05 warmerda + * Fixed memory leak. + * + * Revision 1.8 1997/12/04 15:40:29 warmerda + * Fixed byte swapping of record number, and record length fields in the + * .shp file. + * + * Revision 1.7 1995/10/21 03:15:58 warmerda + * Added support for binary file access, the magic cookie 9997 + * and tried to improve the int32 selection logic for 16bit systems. + * + * Revision 1.6 1995/09/04 04:19:41 warmerda + * Added fix for file bounds. + * + * Revision 1.5 1995/08/25 15:16:44 warmerda + * Fixed a couple of problems with big endian systems ... one with bounds + * and the other with multipart polygons. + * + * Revision 1.4 1995/08/24 18:10:17 warmerda + * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() + * functions (such as on the Sun). + * + * Revision 1.3 1995/08/23 02:23:15 warmerda + * Added support for reading bounds, and fixed up problems in setting the + * file wide bounds. + * + * Revision 1.2 1995/08/04 03:16:57 warmerda + * Added header. + * + */ + +#include "shapefil.h" + +#include <math.h> +#include <limits.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +SHP_CVSID("$Id: shpopen.c,v 1.59 2008/03/14 05:25:31 fwarmerdam Exp $") + +typedef unsigned char uchar; + +#if UINT_MAX == 65535 +typedef long int32; +#else +typedef int int32; +#endif + +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif + +#define ByteCopy( a, b, c ) memcpy( b, a, c ) +#ifndef MAX +# define MIN(a,b) ((a<b) ? a : b) +# define MAX(a,b) ((a>b) ? a : b) +#endif + +static int bBigEndian; + + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + uchar temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((uchar *) wordP)[i]; + ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; + ((uchar *) wordP)[length-i-1] = temp; + } +} + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SHPWriteHeader() */ +/* */ +/* Write out a header for the .shp and .shx files as well as the */ +/* contents of the index (.shx) file. */ +/************************************************************************/ + +void SHPWriteHeader( SHPHandle psSHP ) + +{ + uchar abyHeader[100]; + int i; + int32 i32; + double dValue; + int32 *panSHX; + + if (psSHP->fpSHX == NULL) + { + psSHP->sHooks.Error( "SHPWriteHeader failed : SHX file is closed"); + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare header block for .shp file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 100; i++ ) + abyHeader[i] = 0; + + abyHeader[2] = 0x27; /* magic cookie */ + abyHeader[3] = 0x0a; + + i32 = psSHP->nFileSize/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + i32 = 1000; /* version */ + ByteCopy( &i32, abyHeader+28, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+28 ); + + i32 = psSHP->nShapeType; /* shape type */ + ByteCopy( &i32, abyHeader+32, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+32 ); + + dValue = psSHP->adBoundsMin[0]; /* set bounds */ + ByteCopy( &dValue, abyHeader+36, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+36 ); + + dValue = psSHP->adBoundsMin[1]; + ByteCopy( &dValue, abyHeader+44, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+44 ); + + dValue = psSHP->adBoundsMax[0]; + ByteCopy( &dValue, abyHeader+52, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+52 ); + + dValue = psSHP->adBoundsMax[1]; + ByteCopy( &dValue, abyHeader+60, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+60 ); + + dValue = psSHP->adBoundsMin[2]; /* z */ + ByteCopy( &dValue, abyHeader+68, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+68 ); + + dValue = psSHP->adBoundsMax[2]; + ByteCopy( &dValue, abyHeader+76, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+76 ); + + dValue = psSHP->adBoundsMin[3]; /* m */ + ByteCopy( &dValue, abyHeader+84, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+84 ); + + dValue = psSHP->adBoundsMax[3]; + ByteCopy( &dValue, abyHeader+92, 8 ); + if( bBigEndian ) SwapWord( 8, abyHeader+92 ); + +/* -------------------------------------------------------------------- */ +/* Write .shp file header. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, 0, 0 ) != 0 + || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHP ) != 1 ) + { + psSHP->sHooks.Error( "Failure writing .shp header" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + if( psSHP->sHooks.FSeek( psSHP->fpSHX, 0, 0 ) != 0 + || psSHP->sHooks.FWrite( abyHeader, 100, 1, psSHP->fpSHX ) != 1 ) + { + psSHP->sHooks.Error( "Failure writing .shx header" ); + return; + } + +/* -------------------------------------------------------------------- */ +/* Write out the .shx contents. */ +/* -------------------------------------------------------------------- */ + panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); + + for( i = 0; i < psSHP->nRecords; i++ ) + { + panSHX[i*2 ] = psSHP->panRecOffset[i]/2; + panSHX[i*2+1] = psSHP->panRecSize[i]/2; + if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); + if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); + } + + if( (int)psSHP->sHooks.FWrite( panSHX, sizeof(int32)*2, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) + { + psSHP->sHooks.Error( "Failure writing .shx contents" ); + } + + free( panSHX ); + +/* -------------------------------------------------------------------- */ +/* Flush to disk. */ +/* -------------------------------------------------------------------- */ + psSHP->sHooks.FFlush( psSHP->fpSHP ); + psSHP->sHooks.FFlush( psSHP->fpSHX ); +} + +/************************************************************************/ +/* SHPOpen() */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpen( const char * pszLayer, const char * pszAccess ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPOpenLL( pszLayer, pszAccess, &sHooks ); +} + +/************************************************************************/ +/* SHPOpen() */ +/* */ +/* Open the .shp and .shx files based on the basename of the */ +/* files or either file name. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPOpenLL( const char * pszLayer, const char * pszAccess, SAHooks *psHooks ) + +{ + char *pszFullname, *pszBasename; + SHPHandle psSHP; + + uchar *pabyBuf; + int i; + double dValue; + +/* -------------------------------------------------------------------- */ +/* Ensure the access string is one of the legal ones. We */ +/* ensure the result string indicates binary to avoid common */ +/* problems on Windows. */ +/* -------------------------------------------------------------------- */ + if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 + || strcmp(pszAccess,"r+") == 0 ) + pszAccess = "r+b"; + else + pszAccess = "rb"; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Initialize the info structure. */ +/* -------------------------------------------------------------------- */ + psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); + + psSHP->bUpdated = FALSE; + memcpy( &(psSHP->sHooks), psHooks, sizeof(SAHooks) ); + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the .shp and .shx files. Note that files pulled from */ +/* a PC to Unix with upper case filenames won't work! */ +/* -------------------------------------------------------------------- */ + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.shp", pszBasename ) ; + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + if( psSHP->fpSHP == NULL ) + { + sprintf( pszFullname, "%s.SHP", pszBasename ); + psSHP->fpSHP = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHP == NULL ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s.shp or %s.SHP.", + pszBasename, pszBasename ); +#endif + free( psSHP ); + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + sprintf( pszFullname, "%s.shx", pszBasename ); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + if( psSHP->fpSHX == NULL ) + { + sprintf( pszFullname, "%s.SHX", pszBasename ); + psSHP->fpSHX = psSHP->sHooks.FOpen(pszFullname, pszAccess ); + } + + if( psSHP->fpSHX == NULL ) + { +#ifdef USE_CPL + CPLError( CE_Failure, CPLE_OpenFailed, + "Unable to open %s.shx or %s.SHX.", + pszBasename, pszBasename ); +#endif + psSHP->sHooks.FClose( psSHP->fpSHP ); + free( psSHP ); + free( pszBasename ); + free( pszFullname ); + return( NULL ); + } + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Read the file size from the SHP file. */ +/* -------------------------------------------------------------------- */ + pabyBuf = (uchar *) malloc(100); + psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHP ); + + psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 + + pabyBuf[25] * 256 * 256 + + pabyBuf[26] * 256 + + pabyBuf[27]) * 2; + +/* -------------------------------------------------------------------- */ +/* Read SHX file Header info */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FRead( pabyBuf, 100, 1, psSHP->fpSHX ) != 1 + || pabyBuf[0] != 0 + || pabyBuf[1] != 0 + || pabyBuf[2] != 0x27 + || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) + { + psSHP->sHooks.Error( ".shx file is unreadable, or corrupt." ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); + + return( NULL ); + } + + psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; + psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; + + psSHP->nShapeType = pabyBuf[32]; + + if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) + { + char szError[200]; + + sprintf( szError, + "Record count in .shp header is %d, which seems\n" + "unreasonable. Assuming header is corrupt.", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP ); + free(pabyBuf); + + return( NULL ); + } + +/* -------------------------------------------------------------------- */ +/* Read the bounds. */ +/* -------------------------------------------------------------------- */ + if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); + memcpy( &dValue, pabyBuf+36, 8 ); + psSHP->adBoundsMin[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); + memcpy( &dValue, pabyBuf+44, 8 ); + psSHP->adBoundsMin[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); + memcpy( &dValue, pabyBuf+52, 8 ); + psSHP->adBoundsMax[0] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); + memcpy( &dValue, pabyBuf+60, 8 ); + psSHP->adBoundsMax[1] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ + memcpy( &dValue, pabyBuf+68, 8 ); + psSHP->adBoundsMin[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); + memcpy( &dValue, pabyBuf+76, 8 ); + psSHP->adBoundsMax[2] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ + memcpy( &dValue, pabyBuf+84, 8 ); + psSHP->adBoundsMin[3] = dValue; + + if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); + memcpy( &dValue, pabyBuf+92, 8 ); + psSHP->adBoundsMax[3] = dValue; + + free( pabyBuf ); + +/* -------------------------------------------------------------------- */ +/* Read the .shx file to get the offsets to each record in */ +/* the .shp file. */ +/* -------------------------------------------------------------------- */ + psSHP->nMaxRecords = psSHP->nRecords; + + psSHP->panRecOffset = + (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); + psSHP->panRecSize = + (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); + pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); + + if (psSHP->panRecOffset == NULL || + psSHP->panRecSize == NULL || + pabyBuf == NULL) + { + char szError[200]; + + sprintf(szError, + "Not enough memory to allocate requested memory (nRecords=%d).\n" + "Probably broken SHP file", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + if (psSHP->panRecOffset) free( psSHP->panRecOffset ); + if (psSHP->panRecSize) free( psSHP->panRecSize ); + if (pabyBuf) free( pabyBuf ); + free( psSHP ); + return( NULL ); + } + + if( (int) psSHP->sHooks.FRead( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ) + != psSHP->nRecords ) + { + char szError[200]; + + sprintf( szError, + "Failed to read all values for %d records in .shx file.", + psSHP->nRecords ); + psSHP->sHooks.Error( szError ); + + /* SHX is short or unreadable for some reason. */ + psSHP->sHooks.FClose( psSHP->fpSHP ); + psSHP->sHooks.FClose( psSHP->fpSHX ); + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + free( pabyBuf ); + free( psSHP ); + + return( NULL ); + } + + /* In read-only mode, we can close the SHX now */ + if (strcmp(pszAccess, "rb") == 0) + { + psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->fpSHX = NULL; + } + + for( i = 0; i < psSHP->nRecords; i++ ) + { + int32 nOffset, nLength; + + memcpy( &nOffset, pabyBuf + i * 8, 4 ); + if( !bBigEndian ) SwapWord( 4, &nOffset ); + + memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); + if( !bBigEndian ) SwapWord( 4, &nLength ); + + psSHP->panRecOffset[i] = nOffset*2; + psSHP->panRecSize[i] = nLength*2; + } + free( pabyBuf ); + + return( psSHP ); +} + +/************************************************************************/ +/* SHPClose() */ +/* */ +/* Close the .shp and .shx files. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPClose(SHPHandle psSHP ) + +{ + if( psSHP == NULL ) + return; + +/* -------------------------------------------------------------------- */ +/* Update the header if we have modified anything. */ +/* -------------------------------------------------------------------- */ + if( psSHP->bUpdated ) + SHPWriteHeader( psSHP ); + +/* -------------------------------------------------------------------- */ +/* Free all resources, and close files. */ +/* -------------------------------------------------------------------- */ + free( psSHP->panRecOffset ); + free( psSHP->panRecSize ); + + if ( psSHP->fpSHX != NULL) + psSHP->sHooks.FClose( psSHP->fpSHX ); + psSHP->sHooks.FClose( psSHP->fpSHP ); + + if( psSHP->pabyRec != NULL ) + { + free( psSHP->pabyRec ); + } + + free( psSHP ); +} + +/************************************************************************/ +/* SHPGetInfo() */ +/* */ +/* Fetch general information about the shape file. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, + double * padfMinBound, double * padfMaxBound ) + +{ + int i; + + if( psSHP == NULL ) + return; + + if( pnEntities != NULL ) + *pnEntities = psSHP->nRecords; + + if( pnShapeType != NULL ) + *pnShapeType = psSHP->nShapeType; + + for( i = 0; i < 4; i++ ) + { + if( padfMinBound != NULL ) + padfMinBound[i] = psSHP->adBoundsMin[i]; + if( padfMaxBound != NULL ) + padfMaxBound[i] = psSHP->adBoundsMax[i]; + } +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPCreate( const char * pszLayer, int nShapeType ) + +{ + SAHooks sHooks; + + SASetupDefaultHooks( &sHooks ); + + return SHPCreateLL( pszLayer, nShapeType, &sHooks ); +} + +/************************************************************************/ +/* SHPCreate() */ +/* */ +/* Create a new shape file and return a handle to the open */ +/* shape file with read/write access. */ +/************************************************************************/ + +SHPHandle SHPAPI_CALL +SHPCreateLL( const char * pszLayer, int nShapeType, SAHooks *psHooks ) + +{ + char *pszBasename, *pszFullname; + int i; + SAFile fpSHP, fpSHX; + uchar abyHeader[100]; + int32 i32; + double dValue; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this system. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((uchar *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Compute the base (layer) name. If there is any extension */ +/* on the passed in filename we will strip it off. */ +/* -------------------------------------------------------------------- */ + pszBasename = (char *) malloc(strlen(pszLayer)+5); + strcpy( pszBasename, pszLayer ); + for( i = strlen(pszBasename)-1; + i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' + && pszBasename[i] != '\\'; + i-- ) {} + + if( pszBasename[i] == '.' ) + pszBasename[i] = '\0'; + +/* -------------------------------------------------------------------- */ +/* Open the two files so we can write their headers. */ +/* -------------------------------------------------------------------- */ + pszFullname = (char *) malloc(strlen(pszBasename) + 5); + sprintf( pszFullname, "%s.shp", pszBasename ); + fpSHP = psHooks->FOpen(pszFullname, "wb" ); + if( fpSHP == NULL ) + { + psHooks->Error( "Failed to create file .shp file." ); + return( NULL ); + } + + sprintf( pszFullname, "%s.shx", pszBasename ); + fpSHX = psHooks->FOpen(pszFullname, "wb" ); + if( fpSHX == NULL ) + { + psHooks->Error( "Failed to create file .shx file." ); + return( NULL ); + } + + free( pszFullname ); + free( pszBasename ); + +/* -------------------------------------------------------------------- */ +/* Prepare header block for .shp file. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < 100; i++ ) + abyHeader[i] = 0; + + abyHeader[2] = 0x27; /* magic cookie */ + abyHeader[3] = 0x0a; + + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + i32 = 1000; /* version */ + ByteCopy( &i32, abyHeader+28, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+28 ); + + i32 = nShapeType; /* shape type */ + ByteCopy( &i32, abyHeader+32, 4 ); + if( bBigEndian ) SwapWord( 4, abyHeader+32 ); + + dValue = 0.0; /* set bounds */ + ByteCopy( &dValue, abyHeader+36, 8 ); + ByteCopy( &dValue, abyHeader+44, 8 ); + ByteCopy( &dValue, abyHeader+52, 8 ); + ByteCopy( &dValue, abyHeader+60, 8 ); + +/* -------------------------------------------------------------------- */ +/* Write .shp file header. */ +/* -------------------------------------------------------------------- */ + if( psHooks->FWrite( abyHeader, 100, 1, fpSHP ) != 1 ) + { + psHooks->Error( "Failed to write .shp header." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Prepare, and write .shx file header. */ +/* -------------------------------------------------------------------- */ + i32 = 50; /* file size */ + ByteCopy( &i32, abyHeader+24, 4 ); + if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); + + if( psHooks->FWrite( abyHeader, 100, 1, fpSHX ) != 1 ) + { + psHooks->Error( "Failed to write .shx header." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Close the files, and then open them as regular existing files. */ +/* -------------------------------------------------------------------- */ + psHooks->FClose( fpSHP ); + psHooks->FClose( fpSHX ); + + return( SHPOpenLL( pszLayer, "r+b", psHooks ) ); +} + +/************************************************************************/ +/* _SHPSetBounds() */ +/* */ +/* Compute a bounds rectangle for a shape, and set it into the */ +/* indicated location in the record. */ +/************************************************************************/ + +static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) + +{ + ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); + ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); + ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); + ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); + + if( bBigEndian ) + { + SwapWord( 8, pabyRec + 0 ); + SwapWord( 8, pabyRec + 8 ); + SwapWord( 8, pabyRec + 16 ); + SwapWord( 8, pabyRec + 24 ); + } +} + +/************************************************************************/ +/* SHPComputeExtents() */ +/* */ +/* Recompute the extents of a shape. Automatically done by */ +/* SHPCreateObject(). */ +/************************************************************************/ + +void SHPAPI_CALL +SHPComputeExtents( SHPObject * psObject ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Build extents for this object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nVertices > 0 ) + { + psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; + psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; + psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; + psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); + psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); + psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); + psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); + + psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); + psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); + psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); + psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); + } +} + +/************************************************************************/ +/* SHPCreateObject() */ +/* */ +/* Create a shape object. It should be freed with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPCreateObject( int nSHPType, int nShapeId, int nParts, + const int * panPartStart, const int * panPartType, + int nVertices, const double *padfX, const double *padfY, + const double * padfZ, const double * padfM ) + +{ + SHPObject *psObject; + int i, bHasM, bHasZ; + + psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); + psObject->nSHPType = nSHPType; + psObject->nShapeId = nShapeId; + psObject->bMeasureIsUsed = FALSE; + +/* -------------------------------------------------------------------- */ +/* Establish whether this shape type has M, and Z values. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARCM + || nSHPType == SHPT_POINTM + || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_MULTIPOINTM ) + { + bHasM = TRUE; + bHasZ = FALSE; + } + else if( nSHPType == SHPT_ARCZ + || nSHPType == SHPT_POINTZ + || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPOINTZ + || nSHPType == SHPT_MULTIPATCH ) + { + bHasM = TRUE; + bHasZ = TRUE; + } + else + { + bHasM = FALSE; + bHasZ = FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Capture parts. Note that part type is optional, and */ +/* defaults to ring. */ +/* -------------------------------------------------------------------- */ + if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON + || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM + || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ + || nSHPType == SHPT_MULTIPATCH ) + { + psObject->nParts = MAX(1,nParts); + + psObject->panPartStart = (int *) + malloc(sizeof(int) * psObject->nParts); + psObject->panPartType = (int *) + malloc(sizeof(int) * psObject->nParts); + + psObject->panPartStart[0] = 0; + psObject->panPartType[0] = SHPP_RING; + + for( i = 0; i < nParts; i++ ) + { + psObject->panPartStart[i] = panPartStart[i]; + + if( panPartType != NULL ) + psObject->panPartType[i] = panPartType[i]; + else + psObject->panPartType[i] = SHPP_RING; + } + + if( psObject->panPartStart[0] != 0 ) + psObject->panPartStart[0] = 0; + } + +/* -------------------------------------------------------------------- */ +/* Capture vertices. Note that Z and M are optional, but X and */ +/* Y are not. */ +/* -------------------------------------------------------------------- */ + if( nVertices > 0 ) + { + psObject->padfX = (double *) calloc(sizeof(double),nVertices); + psObject->padfY = (double *) calloc(sizeof(double),nVertices); + psObject->padfZ = (double *) calloc(sizeof(double),nVertices); + psObject->padfM = (double *) calloc(sizeof(double),nVertices); + + assert( padfX != NULL ); + assert( padfY != NULL ); + + for( i = 0; i < nVertices; i++ ) + { + psObject->padfX[i] = padfX[i]; + psObject->padfY[i] = padfY[i]; + if( padfZ != NULL && bHasZ ) + psObject->padfZ[i] = padfZ[i]; + if( padfM != NULL && bHasM ) + psObject->padfM[i] = padfM[i]; + } + if( padfM != NULL && bHasM ) + psObject->bMeasureIsUsed = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Compute the extents. */ +/* -------------------------------------------------------------------- */ + psObject->nVertices = nVertices; + SHPComputeExtents( psObject ); + + return( psObject ); +} + +/************************************************************************/ +/* SHPCreateSimpleObject() */ +/* */ +/* Create a simple (common) shape object. Destroy with */ +/* SHPDestroyObject(). */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPCreateSimpleObject( int nSHPType, int nVertices, + const double * padfX, const double * padfY, + const double * padfZ ) + +{ + return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, + nVertices, padfX, padfY, padfZ, NULL ) ); +} + +/************************************************************************/ +/* SHPWriteObject() */ +/* */ +/* Write out the vertices of a new structure. Note that it is */ +/* only possible to write vertices at the end of the file. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) + +{ + int nRecordOffset, i, nRecordSize=0; + uchar *pabyRec; + int32 i32; + + psSHP->bUpdated = TRUE; + +/* -------------------------------------------------------------------- */ +/* Ensure that shape object matches the type of the file it is */ +/* being written to. */ +/* -------------------------------------------------------------------- */ + assert( psObject->nSHPType == psSHP->nShapeType + || psObject->nSHPType == SHPT_NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure that -1 is used for appends. Either blow an */ +/* assertion, or if they are disabled, set the shapeid to -1 */ +/* for appends. */ +/* -------------------------------------------------------------------- */ + assert( nShapeId == -1 + || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); + + if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) + nShapeId = -1; + +/* -------------------------------------------------------------------- */ +/* Add the new entity to the in memory index. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) + { + psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); + + psSHP->panRecOffset = (int *) + SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); + psSHP->panRecSize = (int *) + SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize record. */ +/* -------------------------------------------------------------------- */ + pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + + psObject->nParts * 8 + 128); + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a Polygon or Arc. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType == SHPT_POLYGON + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARC + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_ARCM + || psObject->nSHPType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i; + + nPoints = psObject->nVertices; + nParts = psObject->nParts; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); + ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); + + nRecordSize = 52; + + /* + * Write part start positions. + */ + ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, + 4 * psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); + nRecordSize += 4; + } + + /* + * Write multipatch part types if needed. + */ + if( psObject->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( pabyRec + nRecordSize, psObject->panPartType, + 4*psObject->nParts ); + for( i = 0; i < psObject->nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); + nRecordSize += 4; + } + } + + /* + * Write the (x,y) vertex values. + */ + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize ); + + if( bBigEndian ) + SwapWord( 8, pabyRec + nRecordSize + 8 ); + + nRecordSize += 2 * 8; + } + + /* + * Write the Z coordinates (if any). + */ + if( psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ + || psObject->nSHPType == SHPT_MULTIPATCH ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + /* + * Write the M values, if any. + */ + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_POLYGONM + || psObject->nSHPType == SHPT_ARCM +#ifndef DISABLE_MULTIPATCH_MEASURE + || psObject->nSHPType == SHPT_MULTIPATCH +#endif + || psObject->nSHPType == SHPT_POLYGONZ + || psObject->nSHPType == SHPT_ARCZ) ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Extract vertices for a MultiPoint. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_MULTIPOINT + || psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM ) + { + int32 nPoints; + int i; + + nPoints = psObject->nVertices; + + _SHPSetBounds( pabyRec + 12, psObject ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + ByteCopy( &nPoints, pabyRec + 44, 4 ); + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); + ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); + } + + nRecordSize = 48 + 16 * psObject->nVertices; + + if( psObject->nSHPType == SHPT_MULTIPOINTZ ) + { + ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_MULTIPOINTZ + || psObject->nSHPType == SHPT_MULTIPOINTM) ) + { + ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + + for( i = 0; i < psObject->nVertices; i++ ) + { + ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Write point. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_POINT + || psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM ) + { + ByteCopy( psObject->padfX, pabyRec + 12, 8 ); + ByteCopy( psObject->padfY, pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); + if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); + + nRecordSize = 28; + + if( psObject->nSHPType == SHPT_POINTZ ) + { + ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + + if( psObject->bMeasureIsUsed + && (psObject->nSHPType == SHPT_POINTZ + || psObject->nSHPType == SHPT_POINTM) ) + { + ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); + if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); + nRecordSize += 8; + } + } + +/* -------------------------------------------------------------------- */ +/* Not much to do for null geometries. */ +/* -------------------------------------------------------------------- */ + else if( psObject->nSHPType == SHPT_NULL ) + { + nRecordSize = 12; + } + + else + { + /* unknown type */ + assert( FALSE ); + } + +/* -------------------------------------------------------------------- */ +/* Establish where we are going to put this record. If we are */ +/* rewriting and existing record, and it will fit, then put it */ +/* back where the original came from. Otherwise write at the end. */ +/* -------------------------------------------------------------------- */ + if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) + { + if( nShapeId == -1 ) + nShapeId = psSHP->nRecords++; + + psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; + psSHP->panRecSize[nShapeId] = nRecordSize-8; + psSHP->nFileSize += nRecordSize; + } + else + { + nRecordOffset = psSHP->panRecOffset[nShapeId]; + psSHP->panRecSize[nShapeId] = nRecordSize-8; + } + +/* -------------------------------------------------------------------- */ +/* Set the shape type, record number, and record size. */ +/* -------------------------------------------------------------------- */ + i32 = nShapeId+1; /* record # */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec, 4 ); + + i32 = (nRecordSize-8)/2; /* record size */ + if( !bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 4, 4 ); + + i32 = psObject->nSHPType; /* shape type */ + if( bBigEndian ) SwapWord( 4, &i32 ); + ByteCopy( &i32, pabyRec + 8, 4 ); + +/* -------------------------------------------------------------------- */ +/* Write out record. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 + || psSHP->sHooks.FWrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) + { + psSHP->sHooks.Error( "Error in psSHP->sHooks.FSeek() or fwrite() writing object to .shp file." ); + free( pabyRec ); + return -1; + } + + free( pabyRec ); + +/* -------------------------------------------------------------------- */ +/* Expand file wide bounds based on this shape. */ +/* -------------------------------------------------------------------- */ + if( psSHP->adBoundsMin[0] == 0.0 + && psSHP->adBoundsMax[0] == 0.0 + && psSHP->adBoundsMin[1] == 0.0 + && psSHP->adBoundsMax[1] == 0.0 ) + { + if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 ) + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0; + } + else + { + psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; + psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; + psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; + psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; + } + } + + for( i = 0; i < psObject->nVertices; i++ ) + { + psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); + psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); + psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); + psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); + psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); + psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); + psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); + psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); + } + + return( nShapeId ); +} + +/************************************************************************/ +/* SHPReadObject() */ +/* */ +/* Read the vertices, parts, and other non-attribute information */ +/* for one shape. */ +/************************************************************************/ + +SHPObject SHPAPI_CALL1(*) +SHPReadObject( SHPHandle psSHP, int hEntity ) + +{ + int nEntitySize, nRequiredSize; + SHPObject *psShape; + char pszErrorMsg[128]; + +/* -------------------------------------------------------------------- */ +/* Validate the record/entity number. */ +/* -------------------------------------------------------------------- */ + if( hEntity < 0 || hEntity >= psSHP->nRecords ) + return( NULL ); + +/* -------------------------------------------------------------------- */ +/* Ensure our record buffer is large enough. */ +/* -------------------------------------------------------------------- */ + nEntitySize = psSHP->panRecSize[hEntity]+8; + if( nEntitySize > psSHP->nBufSize ) + { + psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,nEntitySize); + if (psSHP->pabyRec == NULL) + { + char szError[200]; + + /* Reallocate previous successfull size for following features */ + psSHP->pabyRec = malloc(psSHP->nBufSize); + + sprintf( szError, + "Not enough memory to allocate requested memory (nBufSize=%d). " + "Probably broken SHP file", psSHP->nBufSize ); + psSHP->sHooks.Error( szError ); + return NULL; + } + + /* Only set new buffer size after successfull alloc */ + psSHP->nBufSize = nEntitySize; + } + + /* In case we were not able to reallocate the buffer on a previous step */ + if (psSHP->pabyRec == NULL) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Read the record. */ +/* -------------------------------------------------------------------- */ + if( psSHP->sHooks.FSeek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ) != 0 + || psSHP->sHooks.FRead( psSHP->pabyRec, nEntitySize, 1, + psSHP->fpSHP ) != 1 ) + { + /* + * TODO - mloskot: Consider detailed diagnostics of shape file, + * for example to detect if file is truncated. + */ + + psSHP->sHooks.Error( "Error in fseek() or fread() reading object from .shp file." ); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Allocate and minimally initialize the object. */ +/* -------------------------------------------------------------------- */ + psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); + psShape->nShapeId = hEntity; + psShape->bMeasureIsUsed = FALSE; + + if ( 8 + 4 > nEntitySize ) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); + +/* ==================================================================== */ +/* Extract vertices for a Polygon or Arc. */ +/* ==================================================================== */ + if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC + || psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_POLYGONM + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_ARCM + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + int32 nPoints, nParts; + int i, nOffset; + + if ( 40 + 8 + 4 > nEntitySize ) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* Extract part/point count, and build vertex and part arrays */ +/* to proper size. */ +/* -------------------------------------------------------------------- */ + memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); + memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + if( bBigEndian ) SwapWord( 4, &nParts ); + + if (nPoints < 0 || nParts < 0 || + nPoints > 50 * 1000 * 1000 || nParts > 10 * 1000 * 1000) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d.", + hEntity, nPoints, nParts); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + /* With the previous checks on nPoints and nParts, */ + /* we should not overflow here and after */ + /* since 50 M * (16 + 8 + 8) = 1 600 MB */ + nRequiredSize = 44 + 8 + 4 * nParts + 16 * nPoints; + if ( psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + nRequiredSize += 16 + 8 * nPoints; + } + if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + nRequiredSize += 4 * nParts; + } + if (nRequiredSize > nEntitySize) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d, nPoints=%d, nParts=%d, nEntitySize=%d.", + hEntity, nPoints, nParts, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + psShape->nVertices = nPoints; + psShape->padfX = (double *) calloc(nPoints,sizeof(double)); + psShape->padfY = (double *) calloc(nPoints,sizeof(double)); + psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); + psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + psShape->nParts = nParts; + psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); + psShape->panPartType = (int *) calloc(nParts,sizeof(int)); + + if (psShape->padfX == NULL || + psShape->padfY == NULL || + psShape->padfZ == NULL || + psShape->padfM == NULL || + psShape->panPartStart == NULL || + psShape->panPartType == NULL) + { + snprintf(pszErrorMsg, 128, + "Not enough memory to allocate requested memory (nPoints=%d, nParts=%d) for shape %d. " + "Probably broken SHP file", hEntity, nPoints, nParts ); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + for( i = 0; i < nParts; i++ ) + psShape->panPartType[i] = SHPP_RING; + +/* -------------------------------------------------------------------- */ +/* Copy out the part array from the record. */ +/* -------------------------------------------------------------------- */ + memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); + for( i = 0; i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); + + /* We check that the offset is inside the vertex array */ + if (psShape->panPartStart[i] < 0 || + psShape->panPartStart[i] >= psShape->nVertices) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, nVertices = %d", + hEntity, i, psShape->panPartStart[i], psShape->nVertices); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + if (i > 0 && psShape->panPartStart[i] <= psShape->panPartStart[i-1]) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : panPartStart[%d] = %d, panPartStart[%d] = %d", + hEntity, i, psShape->panPartStart[i], i - 1, psShape->panPartStart[i - 1]); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + } + + nOffset = 44 + 8 + 4*nParts; + +/* -------------------------------------------------------------------- */ +/* If this is a multipatch, we will also have parts types. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); + for( i = 0; i < nParts; i++ ) + { + if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); + } + + nOffset += 4*nParts; + } + +/* -------------------------------------------------------------------- */ +/* Copy out the vertices from the record. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < nPoints; i++ ) + { + memcpy(psShape->padfX + i, + psSHP->pabyRec + nOffset + i * 16, + 8 ); + + memcpy(psShape->padfY + i, + psSHP->pabyRec + nOffset + i * 16 + 8, + 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset += 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_POLYGONZ + || psShape->nSHPType == SHPT_ARCZ + || psShape->nSHPType == SHPT_MULTIPATCH ) + { + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 16 + 8*nPoints ) + { + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + psShape->bMeasureIsUsed = TRUE; + } + } + +/* ==================================================================== */ +/* Extract vertices for a MultiPoint. */ +/* ==================================================================== */ + else if( psShape->nSHPType == SHPT_MULTIPOINT + || psShape->nSHPType == SHPT_MULTIPOINTM + || psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + int32 nPoints; + int i, nOffset; + + if ( 44 + 4 > nEntitySize ) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); + + if( bBigEndian ) SwapWord( 4, &nPoints ); + + if (nPoints < 0 || nPoints > 50 * 1000 * 1000) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d", + hEntity, nPoints); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + nRequiredSize = 48 + nPoints * 16; + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + nRequiredSize += 16 + nPoints * 8; + } + if (nRequiredSize > nEntitySize) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nPoints = %d, nEntitySize = %d", + hEntity, nPoints, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + psShape->nVertices = nPoints; + psShape->padfX = (double *) calloc(nPoints,sizeof(double)); + psShape->padfY = (double *) calloc(nPoints,sizeof(double)); + psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); + psShape->padfM = (double *) calloc(nPoints,sizeof(double)); + + if (psShape->padfX == NULL || + psShape->padfY == NULL || + psShape->padfZ == NULL || + psShape->padfM == NULL) + { + snprintf(pszErrorMsg, 128, + "Not enough memory to allocate requested memory (nPoints=%d) for shape %d. " + "Probably broken SHP file", hEntity, nPoints ); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + + for( i = 0; i < nPoints; i++ ) + { + memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); + memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); + if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); + } + + nOffset = 48 + 16*nPoints; + +/* -------------------------------------------------------------------- */ +/* Get the X/Y bounds. */ +/* -------------------------------------------------------------------- */ + memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); + memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); + memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); + memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_MULTIPOINTZ ) + { + memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfZ + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); + } + + nOffset += 16 + 8*nPoints; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 16 + 8*nPoints ) + { + memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); + memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); + + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); + if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); + + for( i = 0; i < nPoints; i++ ) + { + memcpy( psShape->padfM + i, + psSHP->pabyRec + nOffset + 16 + i*8, 8 ); + if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); + } + psShape->bMeasureIsUsed = TRUE; + } + } + +/* ==================================================================== */ +/* Extract vertices for a point. */ +/* ==================================================================== */ + else if( psShape->nSHPType == SHPT_POINT + || psShape->nSHPType == SHPT_POINTM + || psShape->nSHPType == SHPT_POINTZ ) + { + int nOffset; + + psShape->nVertices = 1; + psShape->padfX = (double *) calloc(1,sizeof(double)); + psShape->padfY = (double *) calloc(1,sizeof(double)); + psShape->padfZ = (double *) calloc(1,sizeof(double)); + psShape->padfM = (double *) calloc(1,sizeof(double)); + + if (20 + 8 + (( psShape->nSHPType == SHPT_POINTZ ) ? 8 : 0)> nEntitySize) + { + snprintf(pszErrorMsg, 128, "Corrupted .shp file : shape %d : nEntitySize = %d", + hEntity, nEntitySize); + psSHP->sHooks.Error( pszErrorMsg ); + SHPDestroyObject(psShape); + return NULL; + } + memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); + memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfX ); + if( bBigEndian ) SwapWord( 8, psShape->padfY ); + + nOffset = 20 + 8; + +/* -------------------------------------------------------------------- */ +/* If we have a Z coordinate, collect that now. */ +/* -------------------------------------------------------------------- */ + if( psShape->nSHPType == SHPT_POINTZ ) + { + memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfZ ); + + nOffset += 8; + } + +/* -------------------------------------------------------------------- */ +/* If we have a M measure value, then read it now. We assume */ +/* that the measure can be present for any shape if the size is */ +/* big enough, but really it will only occur for the Z shapes */ +/* (options), and the M shapes. */ +/* -------------------------------------------------------------------- */ + if( nEntitySize >= nOffset + 8 ) + { + memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); + + if( bBigEndian ) SwapWord( 8, psShape->padfM ); + psShape->bMeasureIsUsed = TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Since no extents are supplied in the record, we will apply */ +/* them from the single vertex. */ +/* -------------------------------------------------------------------- */ + psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; + psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; + psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; + psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; + } + + return( psShape ); +} + +/************************************************************************/ +/* SHPTypeName() */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +SHPTypeName( int nSHPType ) + +{ + switch( nSHPType ) + { + case SHPT_NULL: + return "NullShape"; + + case SHPT_POINT: + return "Point"; + + case SHPT_ARC: + return "Arc"; + + case SHPT_POLYGON: + return "Polygon"; + + case SHPT_MULTIPOINT: + return "MultiPoint"; + + case SHPT_POINTZ: + return "PointZ"; + + case SHPT_ARCZ: + return "ArcZ"; + + case SHPT_POLYGONZ: + return "PolygonZ"; + + case SHPT_MULTIPOINTZ: + return "MultiPointZ"; + + case SHPT_POINTM: + return "PointM"; + + case SHPT_ARCM: + return "ArcM"; + + case SHPT_POLYGONM: + return "PolygonM"; + + case SHPT_MULTIPOINTM: + return "MultiPointM"; + + case SHPT_MULTIPATCH: + return "MultiPatch"; + + default: + return "UnknownShapeType"; + } +} + +/************************************************************************/ +/* SHPPartTypeName() */ +/************************************************************************/ + +const char SHPAPI_CALL1(*) +SHPPartTypeName( int nPartType ) + +{ + switch( nPartType ) + { + case SHPP_TRISTRIP: + return "TriangleStrip"; + + case SHPP_TRIFAN: + return "TriangleFan"; + + case SHPP_OUTERRING: + return "OuterRing"; + + case SHPP_INNERRING: + return "InnerRing"; + + case SHPP_FIRSTRING: + return "FirstRing"; + + case SHPP_RING: + return "Ring"; + + default: + return "UnknownPartType"; + } +} + +/************************************************************************/ +/* SHPDestroyObject() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyObject( SHPObject * psShape ) + +{ + if( psShape == NULL ) + return; + + if( psShape->padfX != NULL ) + free( psShape->padfX ); + if( psShape->padfY != NULL ) + free( psShape->padfY ); + if( psShape->padfZ != NULL ) + free( psShape->padfZ ); + if( psShape->padfM != NULL ) + free( psShape->padfM ); + + if( psShape->panPartStart != NULL ) + free( psShape->panPartStart ); + if( psShape->panPartType != NULL ) + free( psShape->panPartType ); + + free( psShape ); +} + +/************************************************************************/ +/* SHPRewindObject() */ +/* */ +/* Reset the winding of polygon objects to adhere to the */ +/* specification. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) + +{ + int iOpRing, bAltered = 0; + +/* -------------------------------------------------------------------- */ +/* Do nothing if this is not a polygon object. */ +/* -------------------------------------------------------------------- */ + if( psObject->nSHPType != SHPT_POLYGON + && psObject->nSHPType != SHPT_POLYGONZ + && psObject->nSHPType != SHPT_POLYGONM ) + return 0; + + if( psObject->nVertices == 0 || psObject->nParts == 0 ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Process each of the rings. */ +/* -------------------------------------------------------------------- */ + for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) + { + int bInner, iVert, nVertCount, nVertStart, iCheckRing; + double dfSum, dfTestX, dfTestY; + +/* -------------------------------------------------------------------- */ +/* Determine if this ring is an inner ring or an outer ring */ +/* relative to all the other rings. For now we assume the */ +/* first ring is outer and all others are inner, but eventually */ +/* we need to fix this to handle multiple island polygons and */ +/* unordered sets of rings. */ +/* */ +/* -------------------------------------------------------------------- */ + + /* Use point in the middle of segment to avoid testing + * common points of rings. + */ + dfTestX = ( psObject->padfX[psObject->panPartStart[iOpRing]] + + psObject->padfX[psObject->panPartStart[iOpRing] + 1] ) / 2; + dfTestY = ( psObject->padfY[psObject->panPartStart[iOpRing]] + + psObject->padfY[psObject->panPartStart[iOpRing] + 1] ) / 2; + + bInner = FALSE; + for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) + { + int iEdge; + + if( iCheckRing == iOpRing ) + continue; + + nVertStart = psObject->panPartStart[iCheckRing]; + + if( iCheckRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices + - psObject->panPartStart[iCheckRing]; + else + nVertCount = psObject->panPartStart[iCheckRing+1] + - psObject->panPartStart[iCheckRing]; + + for( iEdge = 0; iEdge < nVertCount; iEdge++ ) + { + int iNext; + + if( iEdge < nVertCount-1 ) + iNext = iEdge+1; + else + iNext = 0; + + /* Rule #1: + * Test whether the edge 'straddles' the horizontal ray from the test point (dfTestY,dfTestY) + * The rule #1 also excludes edges collinear with the ray. + */ + if ( ( psObject->padfY[iEdge+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iNext+nVertStart] ) + || ( psObject->padfY[iNext+nVertStart] < dfTestY + && dfTestY <= psObject->padfY[iEdge+nVertStart] ) ) + { + /* Rule #2: + * Test if edge-ray intersection is on the right from the test point (dfTestY,dfTestY) + */ + double const intersect = + ( psObject->padfX[iEdge+nVertStart] + + ( dfTestY - psObject->padfY[iEdge+nVertStart] ) + / ( psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart] ) + * ( psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart] ) ); + + if (intersect < dfTestX) + { + bInner = !bInner; + } + } + } + } /* for iCheckRing */ + +/* -------------------------------------------------------------------- */ +/* Determine the current order of this ring so we will know if */ +/* it has to be reversed. */ +/* -------------------------------------------------------------------- */ + nVertStart = psObject->panPartStart[iOpRing]; + + if( iOpRing == psObject->nParts-1 ) + nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; + else + nVertCount = psObject->panPartStart[iOpRing+1] + - psObject->panPartStart[iOpRing]; + + dfSum = 0.0; + for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) + { + dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] + - psObject->padfY[iVert] * psObject->padfX[iVert+1]; + } + + dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] + - psObject->padfY[iVert] * psObject->padfX[nVertStart]; + +/* -------------------------------------------------------------------- */ +/* Reverse if necessary. */ +/* -------------------------------------------------------------------- */ + if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) + { + int i; + + bAltered++; + for( i = 0; i < nVertCount/2; i++ ) + { + double dfSaved; + + /* Swap X */ + dfSaved = psObject->padfX[nVertStart+i]; + psObject->padfX[nVertStart+i] = + psObject->padfX[nVertStart+nVertCount-i-1]; + psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Y */ + dfSaved = psObject->padfY[nVertStart+i]; + psObject->padfY[nVertStart+i] = + psObject->padfY[nVertStart+nVertCount-i-1]; + psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; + + /* Swap Z */ + if( psObject->padfZ ) + { + dfSaved = psObject->padfZ[nVertStart+i]; + psObject->padfZ[nVertStart+i] = + psObject->padfZ[nVertStart+nVertCount-i-1]; + psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; + } + + /* Swap M */ + if( psObject->padfM ) + { + dfSaved = psObject->padfM[nVertStart+i]; + psObject->padfM[nVertStart+i] = + psObject->padfM[nVertStart+nVertCount-i-1]; + psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; + } + } + } + } + + return bAltered; +} diff --git a/map/shapefile/shptree.c b/map/shapefile/shptree.c new file mode 100644 index 00000000..b8f42a77 --- /dev/null +++ b/map/shapefile/shptree.c @@ -0,0 +1,1047 @@ +/****************************************************************************** + * $Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $ + * + * Project: Shapelib + * Purpose: Implementation of quadtree building and searching functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 1999, Frank Warmerdam + * + * This software is available under the following "MIT Style" license, + * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This + * option is discussed in more detail in shapelib.html. + * + * -- + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************** + * + * $Log: shptree.c,v $ + * Revision 1.12 2008/11/12 15:39:50 fwarmerdam + * improve safety in face of buggy .shp file. + * + * Revision 1.11 2007/10/27 03:31:14 fwarmerdam + * limit default depth of tree to 12 levels (gdal ticket #1594) + * + * Revision 1.10 2005/01/03 22:30:13 fwarmerdam + * added support for saved quadtrees + * + * Revision 1.9 2003/01/28 15:53:41 warmerda + * Avoid build warnings. + * + * Revision 1.8 2002/05/07 13:07:45 warmerda + * use qsort() - patch from Bernhard Herzog + * + * Revision 1.7 2002/01/15 14:36:07 warmerda + * updated email address + * + * Revision 1.6 2001/05/23 13:36:52 warmerda + * added use of SHPAPI_CALL + * + * Revision 1.5 1999/11/05 14:12:05 warmerda + * updated license terms + * + * Revision 1.4 1999/06/02 18:24:21 warmerda + * added trimming code + * + * Revision 1.3 1999/06/02 17:56:12 warmerda + * added quad'' subnode support for trees + * + * Revision 1.2 1999/05/18 19:11:11 warmerda + * Added example searching capability + * + * Revision 1.1 1999/05/18 17:49:20 warmerda + * New + * + */ + +#include "shapefil.h" + +#include <math.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#ifdef USE_CPL +#include <cpl_error.h> +#endif + +#if 0 +SHP_CVSID("$Id: shptree.c,v 1.12 2008/11/12 15:39:50 fwarmerdam Exp $") +#endif + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +static int bBigEndian = 0; + + +/* -------------------------------------------------------------------- */ +/* If the following is 0.5, nodes will be split in half. If it */ +/* is 0.6 then each subnode will contain 60% of the parent */ +/* node, with 20% representing overlap. This can be help to */ +/* prevent small objects on a boundary from shifting too high */ +/* up the tree. */ +/* -------------------------------------------------------------------- */ + +#define SHP_SPLIT_RATIO 0.55 + +/************************************************************************/ +/* SfRealloc() */ +/* */ +/* A realloc cover function that will access a NULL pointer as */ +/* a valid input. */ +/************************************************************************/ + +static void * SfRealloc( void * pMem, int nNewSize ) + +{ + if( pMem == NULL ) + return( (void *) malloc(nNewSize) ); + else + return( (void *) realloc(pMem,nNewSize) ); +} + +/************************************************************************/ +/* SHPTreeNodeInit() */ +/* */ +/* Initialize a tree node. */ +/************************************************************************/ + +static SHPTreeNode *SHPTreeNodeCreate( double * padfBoundsMin, + double * padfBoundsMax ) + +{ + SHPTreeNode *psTreeNode; + + psTreeNode = (SHPTreeNode *) malloc(sizeof(SHPTreeNode)); + if( NULL == psTreeNode ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + return NULL; + } + + psTreeNode->nShapeCount = 0; + psTreeNode->panShapeIds = NULL; + psTreeNode->papsShapeObj = NULL; + + psTreeNode->nSubNodes = 0; + + if( padfBoundsMin != NULL ) + memcpy( psTreeNode->adfBoundsMin, padfBoundsMin, sizeof(double) * 4 ); + + if( padfBoundsMax != NULL ) + memcpy( psTreeNode->adfBoundsMax, padfBoundsMax, sizeof(double) * 4 ); + + return psTreeNode; +} + + +/************************************************************************/ +/* SHPCreateTree() */ +/************************************************************************/ + +SHPTree SHPAPI_CALL1(*) +SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, + double *padfBoundsMin, double *padfBoundsMax ) + +{ + SHPTree *psTree; + + if( padfBoundsMin == NULL && hSHP == NULL ) + return NULL; + +/* -------------------------------------------------------------------- */ +/* Allocate the tree object */ +/* -------------------------------------------------------------------- */ + psTree = (SHPTree *) malloc(sizeof(SHPTree)); + if( NULL == psTree ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + return NULL; + } + + psTree->hSHP = hSHP; + psTree->nMaxDepth = nMaxDepth; + psTree->nDimension = nDimension; + psTree->nTotalCount = 0; + +/* -------------------------------------------------------------------- */ +/* If no max depth was defined, try to select a reasonable one */ +/* that implies approximately 8 shapes per node. */ +/* -------------------------------------------------------------------- */ + if( psTree->nMaxDepth == 0 && hSHP != NULL ) + { + int nMaxNodeCount = 1; + int nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + while( nMaxNodeCount*4 < nShapeCount ) + { + psTree->nMaxDepth += 1; + nMaxNodeCount = nMaxNodeCount * 2; + } + +#ifdef USE_CPL + CPLDebug( "Shape", + "Estimated spatial index tree depth: %d", + psTree->nMaxDepth ); +#endif + + /* NOTE: Due to problems with memory allocation for deep trees, + * automatically estimated depth is limited up to 12 levels. + * See Ticket #1594 for detailed discussion. + */ + if( psTree->nMaxDepth > MAX_DEFAULT_TREE_DEPTH ) + { + psTree->nMaxDepth = MAX_DEFAULT_TREE_DEPTH; + +#ifdef USE_CPL + CPLDebug( "Shape", + "Falling back to max number of allowed index tree levels (%d).", + MAX_DEFAULT_TREE_DEPTH ); +#endif + } + } + +/* -------------------------------------------------------------------- */ +/* Allocate the root node. */ +/* -------------------------------------------------------------------- */ + psTree->psRoot = SHPTreeNodeCreate( padfBoundsMin, padfBoundsMax ); + if( NULL == psTree->psRoot ) + { + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Assign the bounds to the root node. If none are passed in, */ +/* use the bounds of the provided file otherwise the create */ +/* function will have already set the bounds. */ +/* -------------------------------------------------------------------- */ + assert( NULL != psTree ); + assert( NULL != psTree->psRoot ); + + if( padfBoundsMin == NULL ) + { + SHPGetInfo( hSHP, NULL, NULL, + psTree->psRoot->adfBoundsMin, + psTree->psRoot->adfBoundsMax ); + } + +/* -------------------------------------------------------------------- */ +/* If we have a file, insert all it's shapes into the tree. */ +/* -------------------------------------------------------------------- */ + if( hSHP != NULL ) + { + int iShape, nShapeCount; + + SHPGetInfo( hSHP, &nShapeCount, NULL, NULL, NULL ); + + for( iShape = 0; iShape < nShapeCount; iShape++ ) + { + SHPObject *psShape; + + psShape = SHPReadObject( hSHP, iShape ); + if( psShape != NULL ) + { + SHPTreeAddShapeId( psTree, psShape ); + SHPDestroyObject( psShape ); + } + } + } + + return psTree; +} + +/************************************************************************/ +/* SHPDestroyTreeNode() */ +/************************************************************************/ + +static void SHPDestroyTreeNode( SHPTreeNode * psTreeNode ) + +{ + int i; + + assert( NULL != psTreeNode ); + + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + } + + if( psTreeNode->panShapeIds != NULL ) + free( psTreeNode->panShapeIds ); + + if( psTreeNode->papsShapeObj != NULL ) + { + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + if( psTreeNode->papsShapeObj[i] != NULL ) + SHPDestroyObject( psTreeNode->papsShapeObj[i] ); + } + + free( psTreeNode->papsShapeObj ); + } + + free( psTreeNode ); +} + +/************************************************************************/ +/* SHPDestroyTree() */ +/************************************************************************/ + +void SHPAPI_CALL +SHPDestroyTree( SHPTree * psTree ) + +{ + SHPDestroyTreeNode( psTree->psRoot ); + free( psTree ); +} + +/************************************************************************/ +/* SHPCheckBoundsOverlap() */ +/* */ +/* Do the given boxes overlap at all? */ +/************************************************************************/ + +int SHPAPI_CALL +SHPCheckBoundsOverlap( double * padfBox1Min, double * padfBox1Max, + double * padfBox2Min, double * padfBox2Max, + int nDimension ) + +{ + int iDim; + + for( iDim = 0; iDim < nDimension; iDim++ ) + { + if( padfBox2Max[iDim] < padfBox1Min[iDim] ) + return FALSE; + + if( padfBox1Max[iDim] < padfBox2Min[iDim] ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPCheckObjectContained() */ +/* */ +/* Does the given shape fit within the indicated extents? */ +/************************************************************************/ + +static int SHPCheckObjectContained( SHPObject * psObject, int nDimension, + double * padfBoundsMin, double * padfBoundsMax ) + +{ + if( psObject->dfXMin < padfBoundsMin[0] + || psObject->dfXMax > padfBoundsMax[0] ) + return FALSE; + + if( psObject->dfYMin < padfBoundsMin[1] + || psObject->dfYMax > padfBoundsMax[1] ) + return FALSE; + + if( nDimension == 2 ) + return TRUE; + + if( psObject->dfZMin < padfBoundsMin[2] + || psObject->dfZMax < padfBoundsMax[2] ) + return FALSE; + + if( nDimension == 3 ) + return TRUE; + + if( psObject->dfMMin < padfBoundsMin[3] + || psObject->dfMMax < padfBoundsMax[3] ) + return FALSE; + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeSplitBounds() */ +/* */ +/* Split a region into two subregion evenly, cutting along the */ +/* longest dimension. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeSplitBounds( double *padfBoundsMinIn, double *padfBoundsMaxIn, + double *padfBoundsMin1, double * padfBoundsMax1, + double *padfBoundsMin2, double * padfBoundsMax2 ) + +{ +/* -------------------------------------------------------------------- */ +/* The output bounds will be very similar to the input bounds, */ +/* so just copy over to start. */ +/* -------------------------------------------------------------------- */ + memcpy( padfBoundsMin1, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax1, padfBoundsMaxIn, sizeof(double) * 4 ); + memcpy( padfBoundsMin2, padfBoundsMinIn, sizeof(double) * 4 ); + memcpy( padfBoundsMax2, padfBoundsMaxIn, sizeof(double) * 4 ); + +/* -------------------------------------------------------------------- */ +/* Split in X direction. */ +/* -------------------------------------------------------------------- */ + if( (padfBoundsMaxIn[0] - padfBoundsMinIn[0]) + > (padfBoundsMaxIn[1] - padfBoundsMinIn[1]) ) + { + double dfRange = padfBoundsMaxIn[0] - padfBoundsMinIn[0]; + + padfBoundsMax1[0] = padfBoundsMinIn[0] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[0] = padfBoundsMaxIn[0] - dfRange * SHP_SPLIT_RATIO; + } + +/* -------------------------------------------------------------------- */ +/* Otherwise split in Y direction. */ +/* -------------------------------------------------------------------- */ + else + { + double dfRange = padfBoundsMaxIn[1] - padfBoundsMinIn[1]; + + padfBoundsMax1[1] = padfBoundsMinIn[1] + dfRange * SHP_SPLIT_RATIO; + padfBoundsMin2[1] = padfBoundsMaxIn[1] - dfRange * SHP_SPLIT_RATIO; + } +} + +/************************************************************************/ +/* SHPTreeNodeAddShapeId() */ +/************************************************************************/ + +static int +SHPTreeNodeAddShapeId( SHPTreeNode * psTreeNode, SHPObject * psObject, + int nMaxDepth, int nDimension ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* If there are subnodes, then consider wiether this object */ +/* will fit in them. */ +/* -------------------------------------------------------------------- */ + if( nMaxDepth > 1 && psTreeNode->nSubNodes > 0 ) + { + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPCheckObjectContained(psObject, nDimension, + psTreeNode->apsSubNode[i]->adfBoundsMin, + psTreeNode->apsSubNode[i]->adfBoundsMax)) + { + return SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[i], + psObject, nMaxDepth-1, + nDimension ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating four subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 4 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMinH1[4], adfBoundsMaxH1[4]; + double adfBoundsMinH2[4], adfBoundsMaxH2[4]; + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + double adfBoundsMin3[4], adfBoundsMax3[4]; + double adfBoundsMin4[4], adfBoundsMax4[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMinH2, adfBoundsMaxH2 ); + + SHPTreeSplitBounds( adfBoundsMinH1, adfBoundsMaxH1, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + SHPTreeSplitBounds( adfBoundsMinH2, adfBoundsMaxH2, + adfBoundsMin3, adfBoundsMax3, + adfBoundsMin4, adfBoundsMax4 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin3, adfBoundsMax3) + || SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin4, adfBoundsMax4) ) + { + psTreeNode->nSubNodes = 4; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + psTreeNode->apsSubNode[2] = SHPTreeNodeCreate( adfBoundsMin3, + adfBoundsMax3 ); + psTreeNode->apsSubNode[3] = SHPTreeNodeCreate( adfBoundsMin4, + adfBoundsMax4 ); + + /* recurse back on this node now that it has subnodes */ + return( SHPTreeNodeAddShapeId( psTreeNode, psObject, + nMaxDepth, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 4 */ + +/* -------------------------------------------------------------------- */ +/* Otherwise, consider creating two subnodes if could fit into */ +/* them, and adding to the appropriate subnode. */ +/* -------------------------------------------------------------------- */ +#if MAX_SUBNODE == 2 + else if( nMaxDepth > 1 && psTreeNode->nSubNodes == 0 ) + { + double adfBoundsMin1[4], adfBoundsMax1[4]; + double adfBoundsMin2[4], adfBoundsMax2[4]; + + SHPTreeSplitBounds( psTreeNode->adfBoundsMin, psTreeNode->adfBoundsMax, + adfBoundsMin1, adfBoundsMax1, + adfBoundsMin2, adfBoundsMax2 ); + + if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin1, adfBoundsMax1)) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[0], psObject, + nMaxDepth - 1, nDimension ) ); + } + else if( SHPCheckObjectContained(psObject, nDimension, + adfBoundsMin2, adfBoundsMax2) ) + { + psTreeNode->nSubNodes = 2; + psTreeNode->apsSubNode[0] = SHPTreeNodeCreate( adfBoundsMin1, + adfBoundsMax1 ); + psTreeNode->apsSubNode[1] = SHPTreeNodeCreate( adfBoundsMin2, + adfBoundsMax2 ); + + return( SHPTreeNodeAddShapeId( psTreeNode->apsSubNode[1], psObject, + nMaxDepth - 1, nDimension ) ); + } + } +#endif /* MAX_SUBNODE == 2 */ + +/* -------------------------------------------------------------------- */ +/* If none of that worked, just add it to this nodes list. */ +/* -------------------------------------------------------------------- */ + psTreeNode->nShapeCount++; + + psTreeNode->panShapeIds = (int *) + SfRealloc( psTreeNode->panShapeIds, + sizeof(int) * psTreeNode->nShapeCount ); + psTreeNode->panShapeIds[psTreeNode->nShapeCount-1] = psObject->nShapeId; + + if( psTreeNode->papsShapeObj != NULL ) + { + psTreeNode->papsShapeObj = (SHPObject **) + SfRealloc( psTreeNode->papsShapeObj, + sizeof(void *) * psTreeNode->nShapeCount ); + psTreeNode->papsShapeObj[psTreeNode->nShapeCount-1] = NULL; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPTreeAddShapeId() */ +/* */ +/* Add a shape to the tree, but don't keep a pointer to the */ +/* object data, just keep the shapeid. */ +/************************************************************************/ + +int SHPAPI_CALL +SHPTreeAddShapeId( SHPTree * psTree, SHPObject * psObject ) + +{ + psTree->nTotalCount++; + + return( SHPTreeNodeAddShapeId( psTree->psRoot, psObject, + psTree->nMaxDepth, psTree->nDimension ) ); +} + +/************************************************************************/ +/* SHPTreeCollectShapesIds() */ +/* */ +/* Work function implementing SHPTreeFindLikelyShapes() on a */ +/* tree node by tree node basis. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeCollectShapeIds( SHPTree *hTree, SHPTreeNode * psTreeNode, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount, int * pnMaxShapes, + int ** ppanShapeList ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Does this node overlap the area of interest at all? If not, */ +/* return without adding to the list at all. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( psTreeNode->adfBoundsMin, + psTreeNode->adfBoundsMax, + padfBoundsMin, + padfBoundsMax, + hTree->nDimension ) ) + return; + +/* -------------------------------------------------------------------- */ +/* Grow the list to hold the shapes on this node. */ +/* -------------------------------------------------------------------- */ + if( *pnShapeCount + psTreeNode->nShapeCount > *pnMaxShapes ) + { + *pnMaxShapes = (*pnShapeCount + psTreeNode->nShapeCount) * 2 + 20; + *ppanShapeList = (int *) + SfRealloc(*ppanShapeList,sizeof(int) * *pnMaxShapes); + } + +/* -------------------------------------------------------------------- */ +/* Add the local nodes shapeids to the list. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nShapeCount; i++ ) + { + (*ppanShapeList)[(*pnShapeCount)++] = psTreeNode->panShapeIds[i]; + } + +/* -------------------------------------------------------------------- */ +/* Recurse to subnodes if they exist. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( psTreeNode->apsSubNode[i] != NULL ) + SHPTreeCollectShapeIds( hTree, psTreeNode->apsSubNode[i], + padfBoundsMin, padfBoundsMax, + pnShapeCount, pnMaxShapes, + ppanShapeList ); + } +} + +/************************************************************************/ +/* SHPTreeFindLikelyShapes() */ +/* */ +/* Find all shapes within tree nodes for which the tree node */ +/* bounding box overlaps the search box. The return value is */ +/* an array of shapeids terminated by a -1. The shapeids will */ +/* be in order, as hopefully this will result in faster (more */ +/* sequential) reading from the file. */ +/************************************************************************/ + +/* helper for qsort */ +static int +compare_ints( const void * a, const void * b) +{ + return (*(int*)a) - (*(int*)b); +} + +int SHPAPI_CALL1(*) +SHPTreeFindLikelyShapes( SHPTree * hTree, + double * padfBoundsMin, double * padfBoundsMax, + int * pnShapeCount ) + +{ + int *panShapeList=NULL, nMaxShapes = 0; + +/* -------------------------------------------------------------------- */ +/* Perform the search by recursive descent. */ +/* -------------------------------------------------------------------- */ + *pnShapeCount = 0; + + SHPTreeCollectShapeIds( hTree, hTree->psRoot, + padfBoundsMin, padfBoundsMax, + pnShapeCount, &nMaxShapes, + &panShapeList ); + +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + + qsort(panShapeList, *pnShapeCount, sizeof(int), compare_ints); + + return panShapeList; +} + +/************************************************************************/ +/* SHPTreeNodeTrim() */ +/* */ +/* This is the recurve version of SHPTreeTrimExtraNodes() that */ +/* walks the tree cleaning it up. */ +/************************************************************************/ + +static int SHPTreeNodeTrim( SHPTreeNode * psTreeNode ) + +{ + int i; + +/* -------------------------------------------------------------------- */ +/* Trim subtrees, and free subnodes that come back empty. */ +/* -------------------------------------------------------------------- */ + for( i = 0; i < psTreeNode->nSubNodes; i++ ) + { + if( SHPTreeNodeTrim( psTreeNode->apsSubNode[i] ) ) + { + SHPDestroyTreeNode( psTreeNode->apsSubNode[i] ); + + psTreeNode->apsSubNode[i] = + psTreeNode->apsSubNode[psTreeNode->nSubNodes-1]; + + psTreeNode->nSubNodes--; + + i--; /* process the new occupant of this subnode entry */ + } + } + +/* -------------------------------------------------------------------- */ +/* We should be trimmed if we have no subnodes, and no shapes. */ +/* -------------------------------------------------------------------- */ + return( psTreeNode->nSubNodes == 0 && psTreeNode->nShapeCount == 0 ); +} + +/************************************************************************/ +/* SHPTreeTrimExtraNodes() */ +/* */ +/* Trim empty nodes from the tree. Note that we never trim an */ +/* empty root node. */ +/************************************************************************/ + +void SHPAPI_CALL +SHPTreeTrimExtraNodes( SHPTree * hTree ) + +{ + SHPTreeNodeTrim( hTree->psRoot ); +} + +/************************************************************************/ +/* SwapWord() */ +/* */ +/* Swap a 2, 4 or 8 byte word. */ +/************************************************************************/ + +static void SwapWord( int length, void * wordP ) + +{ + int i; + unsigned char temp; + + for( i=0; i < length/2; i++ ) + { + temp = ((unsigned char *) wordP)[i]; + ((unsigned char *)wordP)[i] = ((unsigned char *) wordP)[length-i-1]; + ((unsigned char *) wordP)[length-i-1] = temp; + } +} + +/************************************************************************/ +/* SHPSearchDiskTreeNode() */ +/************************************************************************/ + +static int +SHPSearchDiskTreeNode( FILE *fp, double *padfBoundsMin, double *padfBoundsMax, + int **ppanResultBuffer, int *pnBufferMax, + int *pnResultCount, int bNeedSwap ) + +{ + int i; + int offset; + int numshapes, numsubnodes; + double adfNodeBoundsMin[2], adfNodeBoundsMax[2]; + +/* -------------------------------------------------------------------- */ +/* Read and unswap first part of node info. */ +/* -------------------------------------------------------------------- */ + fread( &offset, 4, 1, fp ); + if ( bNeedSwap ) SwapWord ( 4, &offset ); + + fread( adfNodeBoundsMin, sizeof(double), 2, fp ); + fread( adfNodeBoundsMax, sizeof(double), 2, fp ); + if ( bNeedSwap ) + { + SwapWord( 8, adfNodeBoundsMin + 0 ); + SwapWord( 8, adfNodeBoundsMin + 1 ); + SwapWord( 8, adfNodeBoundsMax + 0 ); + SwapWord( 8, adfNodeBoundsMax + 1 ); + } + + fread( &numshapes, 4, 1, fp ); + if ( bNeedSwap ) SwapWord ( 4, &numshapes ); + +/* -------------------------------------------------------------------- */ +/* If we don't overlap this node at all, we can just fseek() */ +/* pass this node info and all subnodes. */ +/* -------------------------------------------------------------------- */ + if( !SHPCheckBoundsOverlap( adfNodeBoundsMin, adfNodeBoundsMax, + padfBoundsMin, padfBoundsMax, 2 ) ) + { + offset += numshapes*sizeof(int) + sizeof(int); + fseek(fp, offset, SEEK_CUR); + return TRUE; + } + +/* -------------------------------------------------------------------- */ +/* Add all the shapeids at this node to our list. */ +/* -------------------------------------------------------------------- */ + if(numshapes > 0) + { + if( *pnResultCount + numshapes > *pnBufferMax ) + { + *pnBufferMax = (int) ((*pnResultCount + numshapes + 100) * 1.25); + *ppanResultBuffer = (int *) + SfRealloc( *ppanResultBuffer, *pnBufferMax * sizeof(int) ); + } + + fread( *ppanResultBuffer + *pnResultCount, + sizeof(int), numshapes, fp ); + + if (bNeedSwap ) + { + for( i=0; i<numshapes; i++ ) + SwapWord( 4, *ppanResultBuffer + *pnResultCount + i ); + } + + *pnResultCount += numshapes; + } + +/* -------------------------------------------------------------------- */ +/* Process the subnodes. */ +/* -------------------------------------------------------------------- */ + fread( &numsubnodes, 4, 1, fp ); + if ( bNeedSwap ) SwapWord ( 4, &numsubnodes ); + + for(i=0; i<numsubnodes; i++) + { + if( !SHPSearchDiskTreeNode( fp, padfBoundsMin, padfBoundsMax, + ppanResultBuffer, pnBufferMax, + pnResultCount, bNeedSwap ) ) + return FALSE; + } + + return TRUE; +} + +/************************************************************************/ +/* SHPSearchDiskTree() */ +/************************************************************************/ + +int SHPAPI_CALL1(*) +SHPSearchDiskTree( FILE *fp, + double *padfBoundsMin, double *padfBoundsMax, + int *pnShapeCount ) + +{ + int i, bNeedSwap, nBufferMax = 0; + unsigned char abyBuf[16]; + int *panResultBuffer = NULL; + + *pnShapeCount = 0; + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((unsigned char *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + fseek( fp, 0, SEEK_SET ); + fread( abyBuf, 16, 1, fp ); + + if( memcmp( abyBuf, "SQT", 3 ) != 0 ) + return NULL; + + if( (abyBuf[3] == 2 && bBigEndian) + || (abyBuf[3] == 1 && !bBigEndian) ) + bNeedSwap = FALSE; + else + bNeedSwap = TRUE; + +/* -------------------------------------------------------------------- */ +/* Search through root node and it's decendents. */ +/* -------------------------------------------------------------------- */ + if( !SHPSearchDiskTreeNode( fp, padfBoundsMin, padfBoundsMax, + &panResultBuffer, &nBufferMax, + pnShapeCount, bNeedSwap ) ) + { + if( panResultBuffer != NULL ) + free( panResultBuffer ); + *pnShapeCount = 0; + return NULL; + } +/* -------------------------------------------------------------------- */ +/* Sort the id array */ +/* -------------------------------------------------------------------- */ + qsort(panResultBuffer, *pnShapeCount, sizeof(int), compare_ints); + + return panResultBuffer; +} + +/************************************************************************/ +/* SHPGetSubNodeOffset() */ +/* */ +/* Determine how big all the subnodes of this node (and their */ +/* children) will be. This will allow disk based searchers to */ +/* seek past them all efficiently. */ +/************************************************************************/ + +static int SHPGetSubNodeOffset( SHPTreeNode *node) +{ + int i; + long offset=0; + + for(i=0; i<node->nSubNodes; i++ ) + { + if(node->apsSubNode[i]) + { + offset += 4*sizeof(double) + + (node->apsSubNode[i]->nShapeCount+3)*sizeof(int); + offset += SHPGetSubNodeOffset(node->apsSubNode[i]); + } + } + + return(offset); +} + +/************************************************************************/ +/* SHPWriteTreeNode() */ +/************************************************************************/ + +static void SHPWriteTreeNode( FILE *fp, SHPTreeNode *node) +{ + int i,j; + int offset; + unsigned char *pabyRec = NULL; + assert( NULL != node ); + + offset = SHPGetSubNodeOffset(node); + + pabyRec = (unsigned char *) + malloc(sizeof(double) * 4 + + (3 * sizeof(int)) + (node->nShapeCount * sizeof(int)) ); + if( NULL == pabyRec ) + { +#ifdef USE_CPL + CPLError( CE_Fatal, CPLE_OutOfMemory, "Memory allocation failure"); +#endif + assert( 0 ); + } + assert( NULL != pabyRec ); + + memcpy( pabyRec, &offset, 4); + + /* minx, miny, maxx, maxy */ + memcpy( pabyRec+ 4, node->adfBoundsMin+0, sizeof(double) ); + memcpy( pabyRec+12, node->adfBoundsMin+1, sizeof(double) ); + memcpy( pabyRec+20, node->adfBoundsMax+0, sizeof(double) ); + memcpy( pabyRec+28, node->adfBoundsMax+1, sizeof(double) ); + + memcpy( pabyRec+36, &node->nShapeCount, 4); + j = node->nShapeCount * sizeof(int); + memcpy( pabyRec+40, node->panShapeIds, j); + memcpy( pabyRec+j+40, &node->nSubNodes, 4); + + fwrite( pabyRec, 44+j, 1, fp ); + free (pabyRec); + + for(i=0; i<node->nSubNodes; i++ ) + { + if(node->apsSubNode[i]) + SHPWriteTreeNode( fp, node->apsSubNode[i]); + } +} + +/************************************************************************/ +/* SHPWriteTree() */ +/************************************************************************/ + +int SHPWriteTree(SHPTree *tree, const char *filename ) +{ + char signature[4] = "SQT"; + int i; + char abyBuf[32]; + FILE *fp; + +/* -------------------------------------------------------------------- */ +/* Open the output file. */ +/* -------------------------------------------------------------------- */ + fp = fopen(filename, "wb"); + if( fp == NULL ) + { + return FALSE; + } + +/* -------------------------------------------------------------------- */ +/* Establish the byte order on this machine. */ +/* -------------------------------------------------------------------- */ + i = 1; + if( *((unsigned char *) &i) == 1 ) + bBigEndian = FALSE; + else + bBigEndian = TRUE; + +/* -------------------------------------------------------------------- */ +/* Write the header. */ +/* -------------------------------------------------------------------- */ + memcpy( abyBuf+0, signature, 3 ); + + if( bBigEndian ) + abyBuf[3] = 2; /* New MSB */ + else + abyBuf[3] = 1; /* New LSB */ + + abyBuf[4] = 1; /* version */ + abyBuf[5] = 0; /* next 3 reserved */ + abyBuf[6] = 0; + abyBuf[7] = 0; + + fwrite( abyBuf, 8, 1, fp ); + + fwrite( &(tree->nTotalCount), 4, 1, fp ); + + /* write maxdepth */ + + fwrite( &(tree->nMaxDepth), 4, 1, fp ); + +/* -------------------------------------------------------------------- */ +/* Write all the nodes "in order". */ +/* -------------------------------------------------------------------- */ + + SHPWriteTreeNode( fp, tree->psRoot ); + + fclose( fp ); + + return TRUE; +} diff --git a/map/textfile/Makefile.am b/map/textfile/Makefile.am new file mode 100644 index 00000000..a136796f --- /dev/null +++ b/map/textfile/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.inc +AM_CPPFLAGS = @NAVIT_CFLAGS@ -I$(top_srcdir)/navit -DMODULE=map_textfile +if PLUGINS + modulemap_LTLIBRARIES = libmap_textfile.la +else + noinst_LTLIBRARIES = libmap_textfile.la +endif +libmap_textfile_la_SOURCES = textfile.c textfile.h +libmap_textfile_la_LDFLAGS = -module -avoid-version diff --git a/map/textfile/textfile.c b/map/textfile/textfile.c new file mode 100644 index 00000000..2bdf07d9 --- /dev/null +++ b/map/textfile/textfile.c @@ -0,0 +1,405 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <glib.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include "config.h" +#include "debug.h" +#include "plugin.h" +#include "projection.h" +#include "item.h" +#include "map.h" +#include "maptype.h" +#include "attr.h" +#include "transform.h" +#include "file.h" + +#include "textfile.h" + +static int map_id; + +static int +get_tag(char *line, char *name, int *pos, char *ret, char *name_ret) +{ + int len=0,quoted; + char *p,*e,*n; + + dbg(1,"get_tag %s from %s\n", name, line); + if (name) + len=strlen(name); + if (pos) + p=line+*pos; + else + p=line; + for(;;) { + while (*p == ' ') { + p++; + } + if (! *p) + return 0; + n=p; + e=strchr(p,'='); + if (! e) + return 0; + p=e+1; + quoted=0; + while (*p) { + if (*p == ' ' && !quoted) + break; + if (*p == '"') + quoted=1-quoted; + p++; + } + if (name == NULL || (e-n == len && !strncmp(n, name, len))) { + if (name_ret) { + len=e-n; + strncpy(name_ret, n, len); + name_ret[len]='\0'; + } + e++; + len=p-e; + if (e[0] == '"') { + e++; + len-=2; + } + strncpy(ret, e, len); + ret[len]='\0'; + if (pos) + *pos=p-line; + return 1; + } + } + return 0; +} + +static void +get_line(struct map_rect_priv *mr) +{ + if(mr->f) { + if (!mr->m->is_pipe) + mr->pos=ftell(mr->f); + else + mr->pos+=mr->lastlen; + fgets(mr->line, SIZE, mr->f); + mr->lastlen=strlen(mr->line)+1; + if (strlen(mr->line) >= SIZE-1) + printf("line too long\n"); + } +} + +static void +map_destroy_textfile(struct map_priv *m) +{ + dbg(1,"map_destroy_textfile\n"); + g_free(m); +} + +static void +textfile_coord_rewind(void *priv_data) +{ +} + +static int +parse_line(struct map_rect_priv *mr, int attr) +{ + int pos; + + pos=coord_parse(mr->line, projection_mg, &mr->c); + if (pos < strlen(mr->line) && attr) { + strcpy(mr->attrs, mr->line+pos); + } + return pos; +} + +static int +textfile_coord_get(void *priv_data, struct coord *c, int count) +{ + struct map_rect_priv *mr=priv_data; + int ret=0; + dbg(1,"textfile_coord_get %d\n",count); + while (count--) { + if (mr->f && !feof(mr->f) && (!mr->item.id_hi || !mr->eoc) && parse_line(mr, mr->item.id_hi)) { + *c=mr->c; + dbg(1,"c=0x%x,0x%x\n", c->x, c->y); + c++; + ret++; + get_line(mr); + if (mr->item.id_hi) + mr->eoc=1; + } else { + mr->more=0; + break; + } + } + return ret; +} + +static void +textfile_attr_rewind(void *priv_data) +{ +} + +static void +textfile_encode_attr(char *attr_val, enum attr_type attr_type, struct attr *attr) +{ + if (attr_type >= attr_type_int_begin && attr_type <= attr_type_int_end) + attr->u.num=atoi(attr_val); + else + attr->u.str=attr_val; +} + +static int +textfile_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr) +{ + struct map_rect_priv *mr=priv_data; + char *str=NULL; + dbg(1,"textfile_attr_get mr=%p attrs='%s' ", mr, mr->attrs); + if (attr_type != mr->attr_last) { + dbg(1,"reset attr_pos\n"); + mr->attr_pos=0; + mr->attr_last=attr_type; + } + if (attr_type == attr_any) { + dbg(1,"attr_any"); + if (get_tag(mr->attrs,NULL,&mr->attr_pos,mr->attr, mr->attr_name)) { + attr_type=attr_from_name(mr->attr_name); + dbg(1,"found attr '%s' 0x%x\n", mr->attr_name, attr_type); + attr->type=attr_type; + textfile_encode_attr(mr->attr, attr_type, attr); + return 1; + } + } else { + str=attr_to_name(attr_type); + dbg(1,"attr='%s' ",str); + if (get_tag(mr->attrs,str,&mr->attr_pos,mr->attr, NULL)) { + textfile_encode_attr(mr->attr, attr_type, attr); + dbg(1,"found\n"); + return 1; + } + } + dbg(1,"not found\n"); + return 0; +} + +static struct item_methods methods_textfile = { + textfile_coord_rewind, + textfile_coord_get, + textfile_attr_rewind, + textfile_attr_get, +}; + +static struct map_rect_priv * +map_rect_new_textfile(struct map_priv *map, struct map_selection *sel) +{ + struct map_rect_priv *mr; + + dbg(1,"map_rect_new_textfile\n"); + mr=g_new0(struct map_rect_priv, 1); + mr->m=map; + mr->sel=sel; + mr->item.id_hi=0; + mr->item.id_lo=0; + mr->item.meth=&methods_textfile; + mr->item.priv_data=mr; + if (map->is_pipe) { + char *oargs,*args=g_strdup(map->filename),*sep=" "; + enum layer_type lay; + g_free(mr->args); + while (sel) { + oargs=args; + args=g_strdup_printf("%s 0x%x 0x%x 0x%x 0x%x", oargs, sel->u.c_rect.lu.x, sel->u.c_rect.lu.y, sel->u.c_rect.rl.x, sel->u.c_rect.rl.y); + g_free(oargs); + for (lay=layer_town ; lay < layer_end ; lay++) { + oargs=args; + args=g_strdup_printf("%s%s%d", oargs, sep, sel->order); + g_free(oargs); + sep=","; + } + sel=sel->next; + } + dbg(1,"popen args %s\n", args); + mr->args=args; + mr->f=popen(mr->args, "r"); + mr->pos=0; + mr->lastlen=0; + } else { + mr->f=fopen(map->filename, "r"); + } + if(!mr->f) { + printf("map_rect_new_textfile unable to open textfile %s\n",map->filename); + } + get_line(mr); + return mr; +} + + +static void +map_rect_destroy_textfile(struct map_rect_priv *mr) +{ + if (mr->f) { + if (mr->m->is_pipe) + pclose(mr->f); + else + fclose(mr->f); + } + g_free(mr); +} + +static struct item * +map_rect_get_item_textfile(struct map_rect_priv *mr) +{ + char *p,type[SIZE]; + dbg(1,"map_rect_get_item_textfile id_hi=%d line=%s", mr->item.id_hi, mr->line); + if (!mr->f) { + return NULL; + } + while (mr->more) { + struct coord c; + textfile_coord_get(mr, &c, 1); + } + for(;;) { + if (feof(mr->f)) { + dbg(1,"map_rect_get_item_textfile: eof\n"); + if (mr->item.id_hi) { + return NULL; + } + mr->item.id_hi++; + if (mr->m->is_pipe) { + pclose(mr->f); + mr->f=popen(mr->args, "r"); + mr->pos=0; + mr->lastlen=0; + } else + fseek(mr->f, 0, SEEK_SET); + get_line(mr); + } + if ((p=strchr(mr->line,'\n'))) + *p='\0'; + if (mr->item.id_hi) { + mr->attrs[0]='\0'; + if (!parse_line(mr, 1)) { + get_line(mr); + continue; + } + dbg(1,"map_rect_get_item_textfile: point found\n"); + mr->eoc=0; + mr->item.id_lo=mr->pos; + } else { + if (parse_line(mr, 1)) { + get_line(mr); + continue; + } + dbg(1,"map_rect_get_item_textfile: line found\n"); + if (! mr->line[0]) { + get_line(mr); + continue; + } + mr->item.id_lo=mr->pos; + strcpy(mr->attrs, mr->line); + get_line(mr); + dbg(1,"mr=%p attrs=%s\n", mr, mr->attrs); + } + dbg(1,"get_attrs %s\n", mr->attrs); + if (get_tag(mr->attrs,"type",NULL,type,NULL)) { + dbg(1,"type='%s'\n", type); + mr->item.type=item_from_name(type); + if (mr->item.type == type_none) + printf("Warning: type '%s' unknown\n", type); + } else { + get_line(mr); + continue; + } + mr->attr_last=attr_none; + mr->more=1; + dbg(1,"return attr='%s'\n", mr->attrs); + return &mr->item; + } +} + +static struct item * +map_rect_get_item_byid_textfile(struct map_rect_priv *mr, int id_hi, int id_lo) +{ + if (mr->m->is_pipe) { + pclose(mr->f); + mr->f=popen(mr->args, "r"); + mr->pos=0; + mr->lastlen=0; + } else + fseek(mr->f, id_lo, SEEK_SET); + get_line(mr); + mr->item.id_hi=id_hi; + return map_rect_get_item_textfile(mr); +} + +static struct map_methods map_methods_textfile = { + projection_mg, + "iso8859-1", + map_destroy_textfile, + map_rect_new_textfile, + map_rect_destroy_textfile, + map_rect_get_item_textfile, + map_rect_get_item_byid_textfile, +}; + +static struct map_priv * +map_new_textfile(struct map_methods *meth, struct attr **attrs) +{ + struct map_priv *m; + struct attr *data=attr_search(attrs, NULL, attr_data); + struct attr *charset=attr_search(attrs, NULL, attr_charset); + struct file_wordexp *wexp; + int len,is_pipe=0; + char *wdata; + char **wexp_data; + if (! data) + return NULL; + dbg(1,"map_new_textfile %s\n", data->u.str); + wdata=g_strdup(data->u.str); + len=strlen(wdata); + if (len && wdata[len-1] == '|') { + wdata[len-1]='\0'; + is_pipe=1; + } + wexp=file_wordexp_new(wdata); + wexp_data=file_wordexp_get_array(wexp); + *meth=map_methods_textfile; + + m=g_new0(struct map_priv, 1); + m->id=++map_id; + m->filename=g_strdup(wexp_data[0]); + m->is_pipe=is_pipe; + dbg(1,"map_new_textfile %s %s\n", m->filename, wdata); + if (charset) { + m->charset=g_strdup(charset->u.str); + meth->charset=m->charset; + } + file_wordexp_destroy(wexp); + return m; +} + +void +plugin_init(void) +{ + dbg(1,"textfile: plugin_init\n"); + plugin_register_map_type("textfile", map_new_textfile); +} + diff --git a/map/textfile/textfile.h b/map/textfile/textfile.h new file mode 100644 index 00000000..ef97e33a --- /dev/null +++ b/map/textfile/textfile.h @@ -0,0 +1,51 @@ +/** + * Navit, a modular navigation system. + * Copyright (C) 2005-2008 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 <stdio.h> +#include "attr.h" +#include "coord.h" +struct map_priv { + int id; + char *filename; + char *charset; + int is_pipe; +}; + +#define SIZE 512 + +struct map_rect_priv { + struct map_selection *sel; + + FILE *f; + long pos; + char line[SIZE]; + int attr_pos; + enum attr_type attr_last; + char attrs[SIZE]; + char attr[SIZE]; + char attr_name[SIZE]; + struct coord c; + int eoc; + int more; + struct map_priv *m; + struct item item; + char *args; + int lastlen; +}; + |