summaryrefslogtreecommitdiff
path: root/navit/map.c
blob: e3034d429decaa1799402277c7500f2cff46bf57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
/**
 * 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 <glib.h>
#include <string.h>
#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"

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 */
};

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, 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);
    }
}

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,
};