/* Copyright (C) 2007 Alexander Atanasov 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 #include #include #include #include #include #include #include #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 #include "attr.h" #include "coord.h" #include #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; /* 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 0 /* 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, "Don't 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, "Don't 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; } 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, NULL, }; static struct map_priv *gmap_new(struct map_methods *meth, struct attr **attrs, struct callback_list *cbl) { 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_category_map("garmin", gmap_new); }