/** * 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. */ /** @file * * @brief Contains code that makes navit able to load maps * * This file contains the code that makes navit able to load maps. Because * navit is able to open maps in different formats, this code does not handle * any map format itself. This is done by map plugins which register to this * code by calling plugin_register_category_map(). * * When opening a new map, the map plugin will return a pointer to a map_priv * struct, which can be defined by the map plugin and contains whatever private * data the map plugin needs to access the map. This pointer will also be used * as a "handle" to access the map opened. * * A common task is to create a "map rect". A map rect is a rectangular part of * the map, that one can for example retrieve items from. It is not possible to * retrieve items directly from the complete map. Creating a map rect returns a * pointer to a map_rect_priv, which contains private data for the map rect and * will be used as "handle" for this map rect. */ #include #include #include "debug.h" #include "coord.h" #include "projection.h" #include "item.h" #include "map.h" #include "maptype.h" #include "transform.h" #include "plugin.h" #include "callback.h" #include "country.h" #include "xmlconfig.h" /** * @brief Holds information about a map * * This structure holds information about a map. */ struct map { NAVIT_OBJECT struct map_methods meth; /**< Structure with pointers to the map plugin's functions */ struct map_priv *priv; /**< Private data of the map, only known to the map plugin */ struct callback_list *attr_cbl; /**< List of callbacks that are called when attributes change */ }; /** * @brief Describes a rectangular extract of a map * * This structure describes a rectangular extract of a map. */ struct map_rect { struct map *m; /**< The map this extract is from */ struct map_rect_priv *priv; /**< Private data of this map rect, only known to the map plugin */ }; /** * @brief Opens a new map * * This function opens a new map based on the attributes passed. This function * takes the attribute "attr_type" to determine which type of map to open and passes * all attributes to the map plugin's function that was specified in the * plugin_register_new_map_type()-call. * * Note that every plugin should accept an attribute of type "attr_data" to be passed * with the filename of the map to be opened as value. * * @param attrs Attributes specifying which map to open, see description * @return The opened map or NULL on failure */ struct map * map_new(struct attr *parent, struct attr **attrs) { struct map *m; struct map_priv *(*maptype_new)(struct map_methods *meth, struct attr **attrs, struct callback_list *cbl); struct attr *type=attr_search(attrs, NULL, attr_type); if (! type) { dbg(lvl_error,"missing type"); return NULL; } maptype_new=plugin_get_category_map(type->u.str); if (! maptype_new) { dbg(lvl_error,"invalid type '%s'", type->u.str); return NULL; } m=g_new0(struct map, 1); m->attrs=attr_list_dup(attrs); m->func=&map_func; navit_object_ref((struct navit_object *)m); m->attr_cbl=callback_list_new(); m->priv=maptype_new(&m->meth, attrs, m->attr_cbl); if (! m->priv) { map_destroy(m); m=NULL; } return m; } /** * @brief Gets an attribute from a map * * @param this_ The map the attribute should be read from * @param type The type of the attribute to be read * @param attr Pointer to an attrib-structure where the attribute should be written to * @param iter (NOT IMPLEMENTED) Used to iterate through all attributes of a type. Set this to NULL to get the first attribute, set this to an attr_iter to get the next attribute * @return True if the attribute type was found, false if not */ int map_get_attr(struct map *this_, enum attr_type type, struct attr *attr, struct attr_iter *iter) { int ret=0; if (this_->meth.map_get_attr) ret=this_->meth.map_get_attr(this_->priv, type, attr); if (!ret) ret=attr_generic_get_attr(this_->attrs, NULL, type, attr, iter); if (!ret && type == attr_active) { attr->type=type; attr->u.num=1; return 1; } return ret; } /** * @brief Sets an attribute of a map * * This sets an attribute of a map, overwriting an attribute of the same type if it * already exists. This function also calls all the callbacks that are registred * to be called when attributes change. * * @param this_ The map to set the attribute of * @param attr The attribute to set * @return True if the attr could be set, false otherwise */ int map_set_attr(struct map *this_, struct attr *attr) { this_->attrs=attr_generic_set_attr(this_->attrs, attr); if (this_->meth.map_set_attr) this_->meth.map_set_attr(this_->priv, attr); callback_list_call_attr_2(this_->attr_cbl, attr->type, this_, attr); return 1; } /** * @brief Registers a new callback for attribute-change * * This function registers a new callback function that should be called if the attributes * of the map change. * * @param this_ The map to associate the callback with * @param cb The callback to add */ void map_add_callback(struct map *this_, struct callback *cb) { callback_list_add(this_->attr_cbl, cb); } /** * @brief Removes a callback from the list of attribute-change callbacks * * This function removes one callback from the list of callbacks functions that should be called * when attributes of the map change. * * @param this_ The map to remove the callback from * @param cb The callback to remove */ void map_remove_callback(struct map *this_, struct callback *cb) { callback_list_remove(this_->attr_cbl, cb); } /** * @brief Checks if strings from a map have to be converted * * @param this_ Map to be checked for the need to convert strings * @return True if strings from the map have to be converted, false otherwise */ int map_requires_conversion(struct map *this_) { return (this_->meth.charset != NULL && strcmp(this_->meth.charset, "utf-8")); } char *map_converted_string_tmp=NULL; /** * @brief Converts a string from a map into a temporary allocated buffer. Conversion is not performed and original string is returned * if map doesn't require conversion. So lifetime of returned value is very limited. * * @param this_ The map the string to be converted is from * @param str The string to be converted * @return The converted string. Don't care about it after use. */ char *map_convert_string_tmp(struct map *this_, char *str) { if(map_converted_string_tmp!=NULL) g_free(map_converted_string_tmp); map_converted_string_tmp=NULL; if(!this_ || !this_->meth.charset || !strcmp(this_->meth.charset, "utf-8")) return str; map_converted_string_tmp=g_convert(str, -1, "utf-8", this_->meth.charset, NULL, NULL, NULL); if(!map_converted_string_tmp) { dbg(lvl_error,"Error converting '%s' from %s to utf-8", str, this_->meth.charset); return str; } return map_converted_string_tmp; } /** * @brief Converts a string from a map * * @param this_ The map the string to be converted is from * @param str The string to be converted * @return The converted string. It has to be map_convert_free()d after use. */ char *map_convert_string(struct map *this_, char *str) { return map_convert_dup(map_convert_string_tmp(this_,str)); } char *map_convert_dup(char *str) { if(map_converted_string_tmp==str) { map_converted_string_tmp=NULL; return str; } return g_strdup(str); } /** * @brief Frees the memory allocated for a converted string * * @param str The string to be freed */ void map_convert_free(char *str) { g_free(str); } /** * @brief Returns the projection of a map * * @param this_ The map to return the projection of * @return The projection of the map */ enum projection map_projection(struct map *this_) { return this_->meth.pro; } /** * @brief Sets the projection of a map * * @param this_ The map to set the projection of * @param pro The projection to be set */ void map_set_projection(struct map *this_, enum projection pro) { this_->meth.pro=pro; } /** * @brief Destroys an opened map * * @param m The map to be destroyed */ void map_destroy(struct map *m) { if (!m) return; if (m->priv) m->meth.map_destroy(m->priv); attr_list_free(m->attrs); callback_list_destroy(m->attr_cbl); g_free(m); } /** * @brief Creates a new map rect * * This creates a new map rect, which can be used to retrieve items from a map. If * sel is a linked-list of selections, all of them will be used. If you pass NULL as * sel, this means "get me the whole map". * * @param m The map to build the rect on * @param sel Map selection to choose the rectangle - may be NULL, see description * @return A new map rect */ struct map_rect * map_rect_new(struct map *m, struct map_selection *sel) { struct map_rect *mr; #if 0 printf("map_rect_new 0x%x,0x%x-0x%x,0x%x\n", r->lu.x, r->lu.y, r->rl.x, r->rl.y); #endif mr=g_new0(struct map_rect, 1); mr->m=m; mr->priv=m->meth.map_rect_new(m->priv, sel); if (! mr->priv) { g_free(mr); mr=NULL; } return mr; } /** * @brief Gets the next item from a map rect * * Returns an item from a map rect and advances the "item pointer" one step further, * so that at the next call the next item is returned. Returns NULL if there are no more items. * * @param mr The map rect to return an item from * @return An item from the map rect */ struct item * map_rect_get_item(struct map_rect *mr) { struct item *ret; dbg_assert(mr != NULL); dbg_assert(mr->m != NULL); dbg_assert(mr->m->meth.map_rect_get_item != NULL); ret=mr->m->meth.map_rect_get_item(mr->priv); if (ret) ret->map=mr->m; return ret; } /** * @brief Returns the item specified by the ID * * Map drivers may or may not allow multiple items with identical IDs. This function is not guaranteed to be * suitable for iterating over multiple items with identical IDs in the same manner as `map_rect_get_item()`, * as multiple subsequent calls may return items which were already returned by earlier calls. * * If you are working with maps which allow multiple items with identical IDs, the only portable way to * iterate over all items with a given ID is to use `map_rect_get_item()` and skip all items with * non-matching IDs. * * @param mr The map rect to search for the item * @param id_hi High part of the ID to be found * @param id_lo Low part of the ID to be found * @return The item with the specified ID or NULL if not found */ struct item * map_rect_get_item_byid(struct map_rect *mr, int id_hi, int id_lo) { struct item *ret=NULL; dbg_assert(mr != NULL); dbg_assert(mr->m != NULL); if (mr->m->meth.map_rect_get_item_byid) ret=mr->m->meth.map_rect_get_item_byid(mr->priv, id_hi, id_lo); if (ret) ret->map=mr->m; return ret; } /** * @brief Destroys a map rect * * @param mr The map rect to be destroyed */ void map_rect_destroy(struct map_rect *mr) { if (mr) { mr->m->meth.map_rect_destroy(mr->priv); g_free(mr); } } /** * @brief Holds information about a search on a map * * This structure holds information about a search performed on a map. This can be * used as "handle" to retrieve items from a search. */ struct map_search { struct map *m; struct attr search_attr; void *priv; }; /** * @brief Starts a search on a map * * This function starts a search on a map. What attributes one can search for depends on the * map plugin. * * The OSM/binfile plugin currently supports: attr_town_name, attr_street_name * The MG plugin currently supports: ttr_town_postal, attr_town_name, attr_street_name * * If you enable partial matches bear in mind that the search matches only the begin of the * strings - a search for a street named "street" would match to "streetfoo", but not to * "somestreet". Search is case insensitive. * * The item passed to this function specifies a "superior item" to "search within" - e.g. a town * in which we want to search for a street, or a country in which to search for a town. * * Please also note that the search for countries is not handled by map plugins but by navit internally - * have a look into country.c for details. Because of that every map plugin has to accept a country item * to be passed as "superior item". * * Note: If you change something here, please make sure to also update the documentation of mapset_search_new() * in mapset.c! * * @param m The map that should be searched * @param item Specifies a superior item to "search within" (see description) * @param search_attr Attribute specifying what to search for. See description. * @param partial Set this to true to also have partial matches. See description. * @return A new map search struct for this search */ struct map_search * map_search_new(struct map *m, struct item *item, struct attr *search_attr, int partial) { struct map_search *this_; dbg(lvl_debug,"enter(%p,%p,%p,%d)", m, item, search_attr, partial); dbg(lvl_debug,"0x%x 0x%x 0x%x", attr_country_all, search_attr->type, attr_country_name); this_=g_new0(struct map_search,1); this_->m=m; this_->search_attr=*search_attr; if ((search_attr->type >= attr_country_all && search_attr->type <= attr_country_name) || search_attr->type == attr_country_id) this_->priv=country_search_new(&this_->search_attr, partial); else { if (m->meth.map_search_new) { if (m->meth.charset) this_->search_attr.u.str=g_convert(this_->search_attr.u.str, -1,m->meth.charset,"utf-8",NULL,NULL,NULL); this_->priv=m->meth.map_search_new(m->priv, item, &this_->search_attr, partial); if (! this_->priv) { g_free(this_); this_=NULL; } } else { g_free(this_); this_=NULL; } } return this_; } /** * @brief Returns an item from a map search * * This returns an item of the result of a search on a map and advances the "item pointer" one step, * so that at the next call the next item will be returned. If there are no more items in the result * NULL is returned. * * @param this_ Map search struct of the search * @return One item of the result */ struct item * map_search_get_item(struct map_search *this_) { struct item *ret; if (! this_) return NULL; if ((this_->search_attr.type >= attr_country_all && this_->search_attr.type <= attr_country_name) || this_->search_attr.type == attr_country_id) return country_search_get_item(this_->priv); ret=this_->m->meth.map_search_get_item(this_->priv); if (ret) ret->map=this_->m; return ret; } /** * @brief Destroys a map search struct * * @param this_ The map search struct to be destroyed */ void map_search_destroy(struct map_search *this_) { if (! this_) return; if (this_->search_attr.type >= attr_country_all && this_->search_attr.type <= attr_country_name) country_search_destroy(this_->priv); else { if (this_->m->meth.charset) g_free(this_->search_attr.u.str); this_->m->meth.map_search_destroy(this_->priv); } g_free(this_); } /** * @brief Creates a new rectangular map selection * * @param center Coordinates of the center of the new rectangle * @param distance Distance of the rectangle's borders from the center * @param order Desired order of the new selection * @return The new map selection */ struct map_selection * map_selection_rect_new(struct pcoord *center, int distance, int order) { struct map_selection *ret=g_new0(struct map_selection, 1); ret->order=order; ret->range=item_range_all; ret->u.c_rect.lu.x=center->x-distance; ret->u.c_rect.lu.y=center->y+distance; ret->u.c_rect.rl.x=center->x+distance; ret->u.c_rect.rl.y=center->y-distance; return ret; } /** * @brief Duplicates a map selection, transforming coordinates * * This duplicates a map selection and at the same time transforms the internal * coordinates of the selection from one projection to another. * * @param sel The map selection to be duplicated * @param from The projection used for the selection at the moment * @param to The projection that should be used for the duplicated selection * @return A duplicated, transformed map selection */ struct map_selection * map_selection_dup_pro(struct map_selection *sel, enum projection from, enum projection to) { struct map_selection *next,**last; struct map_selection *ret=NULL; last=&ret; while (sel) { next = g_new(struct map_selection, 1); *next=*sel; if (from != projection_none || to != projection_none) { transform_from_to(&sel->u.c_rect.lu, from, &next->u.c_rect.lu, to); transform_from_to(&sel->u.c_rect.rl, from, &next->u.c_rect.rl, to); } *last=next; last=&next->next; sel = sel->next; } return ret; } /** * @brief Duplicates a map selection * * @param sel The map selection to duplicate * @return The duplicated map selection */ struct map_selection * map_selection_dup(struct map_selection *sel) { return map_selection_dup_pro(sel, projection_none, projection_none); } /** * @brief Destroys a map selection * * @param sel The map selection to be destroyed */ void map_selection_destroy(struct map_selection *sel) { struct map_selection *next; while (sel) { next = sel->next; g_free(sel); sel = next; } } /** * @brief Checks if a selection contains a rectangle containing an item * * This function checks if a selection contains a rectangle which exactly contains * an item. The rectangle is automatically built around the given item. * * @param sel The selection to be checked * @param item The item that the rectangle should be built around * @return True if the rectangle is within the selection, false otherwise */ int map_selection_contains_item_rect(struct map_selection *sel, struct item *item) { struct coord c; struct coord_rect r; int count=0; while (item_coord_get(item, &c, 1)) { if (! count) { r.lu=c; r.rl=c; } else coord_rect_extend(&r, &c); count++; } if (! count) return 0; return map_selection_contains_rect(sel, &r); } /** * @brief Checks if a selection contains a item range * * This function checks if a selection contains at least one of the items in range * * @param sel The selection to be checked * @param follow Whether the next pointer of the selection should be followed * @param ranges The item ranges to be checked * @count the number of elements in ranges * @return True if there is a match, false otherwise */ int map_selection_contains_item_range(struct map_selection *sel, int follow, struct item_range *range, int count) { int i; if (! sel) return 1; while (sel) { for (i = 0 ; i < count ; i++) { if (item_range_intersects_range(&sel->range, &range[i])) return 1; } if (! follow) break; sel=sel->next; } return 0; } /** * @brief Checks if a selection contains a item * * This function checks if a selection contains a item type * * @param sel The selection to be checked * @param follow Whether the next pointer of the selection should be followed * @param item The item type to be checked * @return True if there is a match, false otherwise */ int map_selection_contains_item(struct map_selection *sel, int follow, enum item_type type) { if (! sel) return 1; while (sel) { if (item_range_contains_item(&sel->range, type)) return 1; if (! follow) break; sel=sel->next; } return 0; } /** * @brief Checks if a pointer points to the private data of a map * * @param map The map whose private data should be checked. * @param priv The private data that should be checked. * @return True if priv is the private data of map */ int map_priv_is(struct map *map, struct map_priv *priv) { return (map->priv == priv); } void map_dump_filedesc(struct map *map, FILE *out) { struct map_rect *mr=map_rect_new(map, NULL); struct item *item; while ((item = map_rect_get_item(mr))) item_dump_filedesc(item, map, out); map_rect_destroy(mr); } void map_dump_file(struct map *map, const char *file) { FILE *f; f=fopen(file,"w"); if (f) { map_dump_filedesc(map, f); fclose(f); } else dbg(lvl_error,"failed to open file '%s'",file); } void map_dump(struct map *map) { map_dump_filedesc(map, stdout); } struct item * map_rect_create_item(struct map_rect *mr, enum item_type type_) { if(mr && mr->priv && mr->m) { return mr->m->meth.map_rect_create_item(mr->priv, type_) ; } else { return NULL; } } struct object_func map_func = { attr_map, (object_func_new)map_new, (object_func_get_attr)map_get_attr, (object_func_iter_new)NULL, (object_func_iter_destroy)NULL, (object_func_set_attr)map_set_attr, (object_func_add_attr)NULL, (object_func_remove_attr)NULL, (object_func_init)NULL, (object_func_destroy)map_destroy, (object_func_dup)NULL, (object_func_ref)navit_object_ref, (object_func_unref)navit_object_unref, };