summaryrefslogtreecommitdiff
path: root/map/shapefile/shapefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'map/shapefile/shapefile.c')
-rw-r--r--map/shapefile/shapefile.c748
1 files changed, 748 insertions, 0 deletions
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;
+}
+
+