Contents of /trunk/src/map_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 161 - (show annotations)
Sat Apr 11 11:28:56 2009 UTC (15 years, 1 month ago) by harbaum
File MIME type: text/plain
File size: 31622 byte(s)
Prepared 64 bit id support
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21
22 /* --------------------- misc local helper functions ---------------- */
23
24 static void transfer_relations(osm_t *osm, way_t *dst, way_t *src) {
25
26 /* transfer relation memberships from the src way to the dst one */
27 relation_chain_t *rchain = osm_way_to_relation(osm, src);
28
29 while(rchain) {
30 relation_chain_t *next = rchain->next;
31 printf("way #"ITEM_ID_FORMAT" is part of relation #"ITEM_ID_FORMAT"\n",
32 src->id, rchain->relation->id);
33
34 /* make new member of the same relation */
35
36 /* walk member chain. save role of way if its being found. */
37 member_t **member = &rchain->relation->member;
38 char *role = NULL;
39 while(*member) {
40 /* save role of way */
41 if(((*member)->object.type == WAY) && ((*member)->object.way == src))
42 role = (*member)->role;
43 member = &(*member)->next;
44 }
45
46 printf(" adding way #"ITEM_ID_FORMAT" to relation\n", dst->id);
47 *member = g_new0(member_t, 1);
48 (*member)->object.type = WAY;
49 (*member)->object.way = dst;
50 if(role) (*member)->role = g_strdup(role);
51 member = &(*member)->next;
52
53 rchain->relation->flags |= OSM_FLAG_DIRTY;
54
55 g_free(rchain);
56 rchain = next;
57 }
58 }
59
60
61 /* combine tags from src to dst and combine them in a useful manner */
62 /* erase all source tags */
63 static gboolean combine_tags(tag_t **dst, tag_t *src) {
64 gboolean conflict = FALSE;
65 tag_t *dst_orig = *dst;
66
67 /* ---------- transfer tags from way[1] to way[0] ----------- */
68 while(*dst) dst = &((*dst)->next); /* find end of target tag list */
69 while(src) {
70 /* check if same key but with different value is present, */
71 /* ignoring the created_by tags */
72 if(!osm_is_creator_tag(src) &&
73 osm_tag_key_other_value_present(dst_orig, src))
74 conflict = TRUE;
75
76 /* don't copy "created_by" and "source" tag or tags that already */
77 /* exist in identical form */
78 if(osm_is_creator_tag(src) ||
79 osm_tag_key_and_value_present(dst_orig, src)) {
80 tag_t *next = src->next;
81 osm_tag_free(src);
82 src = next;
83 } else {
84 *dst = src;
85 src = src->next;
86 dst = &((*dst)->next);
87 *dst = NULL;
88 }
89 }
90 return conflict;
91 }
92
93 /* -------------------------- way_add ----------------------- */
94
95 void map_edit_way_add_begin(map_t *map, way_t *way_sel) {
96 if(way_sel) printf("previously selected way is #"ITEM_ID_FORMAT"\n",
97 way_sel->id);
98
99 g_assert(!map->action.way);
100 map->action.way = osm_way_new();
101 map->action.extending = NULL;
102 }
103
104 void map_edit_way_add_segment(map_t *map, gint x, gint y) {
105
106 /* convert mouse position to canvas (world) position */
107 canvas_window2world(map->canvas, x, y, &x, &y);
108
109 /* check if this was a double click. This is the case if */
110 /* the last node placed is less than 5 pixels from the current */
111 /* position */
112 node_t *lnode = osm_way_get_last_node(map->action.way);
113 if(lnode && (map->state->zoom * sqrt((lnode->lpos.x-x)*(lnode->lpos.x-x)+
114 (lnode->lpos.y-y)*(lnode->lpos.y-y))) < 5) {
115 #if 0
116 printf("detected double click -> simulate ok click\n");
117 map_hl_touchnode_clear(map);
118 map_action_ok(map->appdata);
119 #else
120 printf("detected double click -> ignore it as accidential\n");
121 #endif
122 } else {
123
124 /* use the existing node if one was touched */
125 node_t *node = map_hl_touchnode_get_node(map);
126 if(node) {
127 printf(" re-using node #"ITEM_ID_FORMAT"\n", node->id);
128 map_hl_touchnode_clear(map);
129
130 g_assert(map->action.way);
131
132 /* check whether this node is first or last one of a different way */
133 way_t *touch_way = NULL;
134 way_chain_t *way_chain = osm_node_to_way(map->appdata->osm, node);
135 while(way_chain) {
136 way_chain_t *next = way_chain->next;
137
138 if(node == osm_way_get_last_node(way_chain->way)) {
139 printf(" way #"ITEM_ID_FORMAT" ends with this node\n",
140 way_chain->way->id);
141 if(!touch_way) touch_way = way_chain->way;
142 }
143
144 if(node == osm_way_get_first_node(way_chain->way)) {
145 printf(" way #"ITEM_ID_FORMAT" starts with this node\n",
146 way_chain->way->id);
147 if(!touch_way) touch_way = way_chain->way;
148 }
149
150 g_free(way_chain);
151 way_chain = next;
152 }
153
154 /* remeber this way as this may be the last node placed */
155 /* and we might want to join this with this other way */
156 map->action.ends_on = touch_way;
157
158 /* is this the first node the user places? */
159 if(!map->action.way->node_chain) {
160 map->action.extending = touch_way;
161
162 if(map->action.extending) {
163 if(!yes_no_f(GTK_WIDGET(map->appdata->window),
164 map->appdata, MISC_AGAIN_ID_EXTEND_WAY, 0,
165 _("Extend way?"),
166 _("Do you want to extend the way present at this location?")))
167 map->action.extending = NULL;
168 else
169 /* there are immediately enough nodes for a valid way */
170 icon_bar_map_cancel_ok(map->appdata, TRUE, TRUE);
171 }
172 }
173
174 } else {
175 /* the current way doesn't end on another way if we are just placing */
176 /* a new node */
177 map->action.ends_on = NULL;
178
179 if(!osm_position_within_bounds(map->appdata->osm, x, y))
180 map_outside_error(map->appdata);
181 else
182 node = osm_node_new(map->appdata->osm, x, y);
183 }
184
185 if(node) {
186 g_assert(map->action.way);
187 osm_way_append_node(map->action.way, node);
188
189 switch(osm_node_chain_length(map->action.way->node_chain)) {
190 case 1:
191 /* replace "place first node..." message */
192 statusbar_set(map->appdata, _("Place next node of way"), FALSE);
193 break;
194
195 case 2:
196 /* two nodes are enough for a valid way */
197 icon_bar_map_cancel_ok(map->appdata, TRUE, TRUE);
198 break;
199
200 default:
201 break;
202 }
203
204 /* remove prior version of this way */
205 map_item_chain_destroy(&map->action.way->map_item_chain);
206
207 /* draw current way */
208 josm_elemstyles_colorize_way(map->style, map->action.way);
209 map_way_draw(map, map->action.way);
210 }
211 }
212 }
213
214 void map_edit_way_add_cancel(map_t *map) {
215
216 printf(" removing temporary way\n");
217 g_assert(map->action.way);
218
219 /* remove all nodes that have been created for this way */
220 /* (their way count will be 0 after removing the way) */
221 node_chain_t *chain = map->action.way->node_chain;
222 while(chain) {
223 node_chain_t *next = chain->next;
224
225 printf(" node #"ITEM_ID_FORMAT" (used by %d)\n",
226 chain->node->id, chain->node->ways);
227
228 chain->node->ways--;
229 if(!chain->node->ways && (chain->node->id == ID_ILLEGAL)) {
230 printf(" -> freeing temp node\n");
231 osm_node_free(&map->appdata->icon, chain->node);
232 }
233 g_free(chain);
234 chain = next;
235 }
236 map->action.way->node_chain = NULL;
237
238 /* remove ways visual representation */
239 map_item_chain_destroy(&map->action.way->map_item_chain);
240
241 osm_way_free(map->action.way);
242 map->action.way = NULL;
243 }
244
245 void map_edit_way_add_ok(map_t *map) {
246 g_assert(map->action.way);
247
248 /* transfer all nodes that have been created for this way */
249 /* into the node chain */
250
251 /* (their way count will be 0 after removing the way) */
252 node_chain_t *chain = map->action.way->node_chain;
253 while(chain) {
254 printf(" node #"ITEM_ID_FORMAT" (used by %d)\n",
255 chain->node->id, chain->node->ways);
256
257 /* a node may have been a stand-alone node before, so remove its */
258 /* visual representation as its now drawn as part of the way */
259 /* (if at all) */
260 if(chain->node->id != ID_ILLEGAL)
261 map_item_chain_destroy(&chain->node->map_item_chain);
262
263 map_node_draw(map, chain->node);
264
265 /* we can be sure that no node gets inserted twice (even if twice in */
266 /* the ways chain) because it gets assigned a non-ID_ILLEGAL id when */
267 /* being moved to the osm node chain */
268 if(chain->node->id == ID_ILLEGAL)
269 osm_node_attach(map->appdata->osm, chain->node);
270
271 chain = chain->next;
272 }
273
274 /* attach to existing way if the user requested so */
275 gboolean reverse = FALSE;
276 if(map->action.extending) {
277 node_t *nfirst = map->action.way->node_chain->node;
278
279 printf(" request to extend way #"ITEM_ID_FORMAT"\n",
280 map->action.extending->id);
281
282 if(osm_way_get_first_node(map->action.extending) == nfirst) {
283 printf(" need to prepend\n");
284 osm_way_reverse(map->action.extending);
285 reverse = TRUE;
286 } else if(osm_way_get_last_node(map->action.extending) == nfirst)
287 printf(" need to append\n");
288
289 /* search end of way to be extended */
290 node_chain_t *chain = map->action.extending->node_chain;
291 g_assert(chain);
292 while(chain->next) chain = chain->next;
293
294 /* skip first node of new way as its the same as the last one of the */
295 /* way we are attaching it to */
296 chain->next = map->action.way->node_chain->next;
297
298 /* terminate new way afer first node */
299 map->action.way->node_chain->next = NULL;
300
301 /* erase and free new way (now only containing the first node anymore) */
302 map_item_chain_destroy(&map->action.way->map_item_chain);
303 osm_way_free(map->action.way);
304
305 map->action.way = map->action.extending;
306 map->action.way->flags |= OSM_FLAG_DIRTY;
307
308 /* and undo reversion of required */
309 if(reverse)
310 osm_way_reverse(map->action.way);
311
312 } else {
313 /* now move the way itself into the main data structure */
314 osm_way_attach(map->appdata->osm, map->action.way);
315 }
316
317 /* we might already be working on the "ends_on" way as we may */
318 /* be extending it. Joining the same way doesn't make sense. */
319 if(map->action.ends_on && (map->action.ends_on == map->action.way)) {
320 printf(" the new way ends on itself -> don't join itself\n");
321 map->action.ends_on = NULL;
322 }
323
324 if(map->action.ends_on)
325 if(!yes_no_f(GTK_WIDGET(map->appdata->window),
326 map->appdata, MISC_AGAIN_ID_EXTEND_WAY_END, 0,
327 _("Join way?"),
328 _("Do you want to join the way present at this location?")))
329 map->action.ends_on = NULL;
330
331 if(map->action.ends_on) {
332 printf(" this new way ends on another way\n");
333
334 /* If reverse is true the node in question is the first one */
335 /* of the newly created way. Thus it is reversed again before */
336 /* attaching and the result is finally reversed once more */
337
338 /* this is slightly more complex as this time two full tagged */
339 /* ways may be involved as the new way may be an extended existing */
340 /* way being connected to another way. This happens if you connect */
341 /* two existing ways using a new way between them */
342
343 if (reverse) osm_way_reverse(map->action.way);
344
345 /* and open dialog to resolve tag collisions if necessary */
346 if(combine_tags(&map->action.way->tag, map->action.ends_on->tag))
347 messagef(GTK_WIDGET(map->appdata->window), _("Way tag conflict"),
348 _("The resulting way contains some conflicting tags. "
349 "Please solve these."));
350
351 map->action.ends_on->tag = NULL;
352
353 /* make way member of all relations ends_on already is */
354 transfer_relations(map->appdata->osm, map->action.way, map->action.ends_on);
355
356 /* check if we have to reverse (again?) to match the way order */
357 if(osm_way_get_last_node(map->action.ends_on) ==
358 osm_way_get_last_node(map->action.way)) {
359
360 printf(" need to prepend ends_on\n");
361
362 /* need to reverse ends_on way */
363 osm_way_reverse(map->action.ends_on);
364 reverse = !reverse;
365 }
366
367 /* search end of node chain */
368 node_chain_t *chain = map->action.way->node_chain;
369 g_assert(chain);
370 while(chain->next) chain = chain->next;
371
372 /* attach ends_on way to way but omit first node */
373 chain->next = map->action.ends_on->node_chain->next;
374 map->action.ends_on->node_chain->next = NULL;
375
376 /* erase and free ends_on (now only containing the first node anymore) */
377 map_way_delete(map->appdata, map->action.ends_on);
378
379 if(reverse) osm_way_reverse(map->action.way);
380 }
381
382 /* remove prior version of this way */
383 map_item_chain_destroy(&map->action.way->map_item_chain);
384
385 /* draw the updated way */
386 map_way_draw(map, map->action.way);
387
388 map_way_select(map->appdata, map->action.way);
389
390 map->action.way = NULL;
391
392 /* let the user specify some tags for the new way */
393 info_dialog(GTK_WIDGET(map->appdata->window), map->appdata, NULL);
394 }
395
396 /* -------------------------- way_node_add ----------------------- */
397
398 void map_edit_way_node_add_highlight(map_t *map, map_item_t *item,
399 gint x, gint y) {
400 if(map_item_is_selected_way(map, item)) {
401 gint nx, ny;
402 canvas_window2world(map->canvas, x, y, &nx, &ny);
403 if(canvas_item_get_segment(item->item, nx, ny) >= 0)
404 map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
405 }
406 }
407
408 void map_edit_way_node_add(map_t *map, gint x, gint y) {
409 /* check if we are still hovering above the selected way */
410 map_item_t *item = map_item_at(map, x, y);
411 if(item && map_item_is_selected_way(map, item)) {
412 /* convert mouse position to canvas (world) position */
413 canvas_window2world(map->canvas, x, y, &x, &y);
414 gint insert_after = canvas_item_get_segment(item->item, x, y)+1;
415 if(insert_after > 0) {
416 /* create new node */
417 node_t* node = osm_node_new(map->appdata->osm, x, y);
418 osm_node_attach(map->appdata->osm, node);
419
420 /* insert it into ways chain of nodes */
421 way_t *way = item->object.way;
422
423 /* search correct position */
424 node_chain_t **chain = &way->node_chain;
425 while(insert_after--) {
426 g_assert(*chain);
427 chain = &(*chain)->next;
428 }
429
430 /* and actually do the insertion */
431 node_chain_t *new_chain_item = g_new0(node_chain_t, 1);
432 new_chain_item->node = node;
433 new_chain_item->next = *chain;
434 *chain = new_chain_item;
435
436 /* clear selection */
437 map_item_deselect(map->appdata);
438
439 /* remove prior version of this way */
440 map_item_chain_destroy(&way->map_item_chain);
441
442 /* draw the updated way */
443 map_way_draw(map, way);
444
445 /* remember that this node is contained in one way */
446 node->ways=1;
447
448 /* and that the way needs to be uploaded */
449 way->flags |= OSM_FLAG_DIRTY;
450
451 /* put gui into idle state */
452 map_action_set(map->appdata, MAP_ACTION_IDLE);
453
454 /* and redo it */
455 map_way_select(map->appdata, way);
456 }
457 }
458 }
459
460 /* -------------------------- way_node_cut ----------------------- */
461
462 void map_edit_way_cut_highlight(map_t *map, map_item_t *item, gint x, gint y) {
463
464 if(map_item_is_selected_way(map, item)) {
465 gint nx, ny, seg;
466 canvas_window2world(map->canvas, x, y, &nx, &ny);
467 seg = canvas_item_get_segment(item->item, nx, ny);
468 if(seg >= 0) {
469 gint x0, y0, x1, y1;
470 canvas_item_get_segment_pos(item->item, seg, &x0, &y0, &x1, &y1);
471
472 gint width = (item->object.way->draw.flags &
473 OSM_DRAW_FLAG_BG)?
474 2*item->object.way->draw.bg.width:
475 3*item->object.way->draw.width;
476 map_hl_segment_draw(map, width, x0, y0, x1, y1);
477 }
478 } else if(map_item_is_selected_node(map, item)) {
479 node_t *nfirst = osm_way_get_first_node(map->selected.object.way);
480 node_t *nlast = osm_way_get_last_node(map->selected.object.way);
481
482 /* cutting a way at its first or last node doesn't make much sense ... */
483 if((nfirst != item->object.node) && (nlast != item->object.node))
484 map_hl_cursor_draw(map, item->object.node->lpos.x, item->object.node->lpos.y,
485 TRUE, 2*map->style->node.radius);
486 }
487 }
488
489 /* cut the currently selected way at the current cursor position */
490 void map_edit_way_cut(map_t *map, gint x, gint y) {
491
492 /* check if we are still hovering above the selected way */
493 map_item_t *item = map_item_at(map, x, y);
494 if(item && (map_item_is_selected_way(map, item) ||
495 map_item_is_selected_node(map, item))) {
496 gboolean cut_at_node = map_item_is_selected_node(map, item);
497
498 /* convert mouse position to canvas (world) position */
499 canvas_window2world(map->canvas, x, y, &x, &y);
500
501 gint cut_at = -1;
502 way_t *way = NULL;
503 if(cut_at_node) {
504 printf(" cut at node\n");
505
506 /* node must not be first or last node of way */
507 g_assert(map->selected.object.type == WAY);
508
509 if((osm_way_get_first_node(map->selected.object.way) !=
510 item->object.node) &&
511 (osm_way_get_last_node(map->selected.object.way) !=
512 item->object.node)) {
513 way = map->selected.object.way;
514
515 cut_at = 0;
516 node_chain_t *chain = way->node_chain;
517 while(chain && chain->node != item->object.node) {
518 chain = chain->next;
519 cut_at++;
520 }
521
522 } else
523 printf(" won't cut as it's last or first node\n");
524
525 } else {
526 printf(" cut at segment\n");
527 cut_at = canvas_item_get_segment(item->item, x, y);
528 if(cut_at >= 0) way = item->object.way;
529 }
530
531 if(way) {
532 /* create a duplicate of the currently selected way */
533 way_t *new = osm_way_new();
534
535 /* if this is a closed way, reorder (rotate) it, so the */
536 /* place to cut is adjecent to the begin/end of the way. */
537 /* this prevents a cut polygon to be split into two ways */
538 g_assert(way->node_chain);
539 if(way->node_chain->node == osm_way_get_last_node(way)) {
540 printf("CLOSED WAY -> rotate by %d\n", cut_at);
541 osm_way_rotate(way, cut_at);
542 cut_at = 0;
543 }
544
545 /* ------------ copy all tags ------------- */
546 new->tag = osm_tags_copy(way->tag, TRUE);
547
548 /* ---- transfer relation membership from way to new ----- */
549 transfer_relations(map->appdata->osm, new, way);
550
551 /* move parts of node_chain to the new way */
552 printf(" moving everthing after segment %d to new way\n", cut_at);
553
554 node_chain_t *chain = way->node_chain;
555 while(cut_at--) {
556 g_assert(chain);
557 chain = chain->next;
558 }
559
560 /* attach remaining nodes to new way */
561 new->node_chain = chain->next;
562
563 /* terminate remainig chain on old way */
564 chain->next = NULL;
565
566 /* if we cut at a node, this node is now part of both ways. so */
567 /* create a copy of the last node of the old way and prepend it to */
568 /* the new way */
569 if(cut_at_node) {
570 node_chain_t *first = g_new0(node_chain_t, 1);
571 first->next = new->node_chain;
572 first->node = osm_way_get_last_node(way);
573 first->node->ways++;
574 new->node_chain = first;
575 }
576
577 /* now move the way itself into the main data structure */
578 osm_way_attach(map->appdata->osm, new);
579
580 /* clear selection */
581 map_item_deselect(map->appdata);
582
583 /* remove prior version of this way */
584 printf("remove visible version of way #"ITEM_ID_FORMAT"\n", way->id);
585 map_item_chain_destroy(&way->map_item_chain);
586
587 /* swap chains of the old way is to be destroyed due to a lack */
588 /* of nodes */
589 if(osm_way_number_of_nodes(way) < 2) {
590 printf("swapping ways to avoid destruction of original way\n");
591 node_chain_t *tmp = way->node_chain;
592 way->node_chain = new->node_chain;
593 new->node_chain = tmp;
594 }
595
596 /* the way may still only consist of a single node. */
597 /* remove it then */
598 if(osm_way_number_of_nodes(way) < 2) {
599 printf("original way has less than 2 nodes left, deleting it\n");
600 map_way_delete(map->appdata, way);
601 item = NULL;
602 } else {
603 printf("original way still has %d nodes\n",
604 osm_way_number_of_nodes(way));
605
606 /* draw the updated old way */
607 josm_elemstyles_colorize_way(map->style, way);
608 map_way_draw(map, way);
609
610 /* remember that the way needs to be uploaded */
611 way->flags |= OSM_FLAG_DIRTY;
612 }
613
614 if(osm_way_number_of_nodes(new) < 2) {
615 printf("new way has less than 2 nodes, deleting it\n");
616 map_way_delete(map->appdata, new);
617 new = NULL;
618 } else {
619
620 /* colorize the new way before drawing */
621 josm_elemstyles_colorize_way(map->style, new);
622 map_way_draw(map, new);
623 }
624
625 /* put gui into idle state */
626 map_action_set(map->appdata, MAP_ACTION_IDLE);
627
628 /* and redo selection if way still exists */
629 if(item)
630 map_way_select(map->appdata, way);
631 else if(new)
632 map_way_select(map->appdata, new);
633 }
634 }
635 }
636
637 void map_edit_node_move(appdata_t *appdata, map_item_t *map_item,
638 gint ex, gint ey) {
639
640 map_t *map = appdata->map;
641 osm_t *osm = appdata->osm;
642
643 g_assert(map_item->object.type == NODE);
644 node_t *node = map_item->object.node;
645
646 printf("released dragged node #"ITEM_ID_FORMAT"\n", node->id);
647 printf(" was at %d %d (%f %f)\n",
648 node->lpos.x, node->lpos.y,
649 node->pos.lat, node->pos.lon);
650
651
652 /* check if it was dropped onto another node */
653 node_t *touchnode = map_hl_touchnode_get_node(map);
654 gboolean joined_with_touchnode = FALSE;
655
656 if(touchnode) {
657 map_hl_touchnode_clear(map);
658
659 printf(" dropped onto node #"ITEM_ID_FORMAT"\n", touchnode->id);
660
661 if(yes_no_f(GTK_WIDGET(appdata->window),
662 appdata, MISC_AGAIN_ID_JOIN_NODES, 0,
663 _("Join nodes?"),
664 _("Do you want to join the dragged node with the one "
665 "you dropped it on?"))) {
666
667 /* the touchnode vanishes and is replaced by the node the */
668 /* user dropped onto it */
669 joined_with_touchnode = TRUE;
670
671 /* use touchnodes position */
672 node->lpos = touchnode->lpos;
673 node->pos = touchnode->pos;
674
675 way_t *way = appdata->osm->way;
676 while(way) {
677 node_chain_t *chain = way->node_chain;
678 while(chain) {
679 if(chain->node == touchnode) {
680 printf(" found node in way #"ITEM_ID_FORMAT"\n", way->id);
681
682 /* replace by node */
683 chain->node = node;
684
685 /* and adjust way references of both nodes */
686 node->ways++;
687 touchnode->ways--;
688
689 way->flags |= OSM_FLAG_DIRTY;
690 }
691 chain = chain->next;
692 }
693 way = way->next;
694 }
695
696 /* replace node in relations */
697 relation_t *relation = appdata->osm->relation;
698 while(relation) {
699 member_t *member = relation->member;
700 while(member) {
701 if(member->object.type == NODE && member->object.node == touchnode) {
702 printf(" found node in relation #"ITEM_ID_FORMAT"\n",
703 relation->id);
704
705 /* replace by node */
706 member->object.node = node;
707
708 relation->flags |= OSM_FLAG_DIRTY;
709 }
710 member = member->next;
711 }
712 relation = relation->next;
713 }
714
715 gboolean conflict = combine_tags(&node->tag, touchnode->tag);
716 touchnode->tag = NULL;
717
718 /* touchnode must not have any references to ways anymore */
719 g_assert(!touchnode->ways);
720
721 /* delete touchnode */
722 /* remove it visually from the screen */
723 map_item_chain_destroy(&touchnode->map_item_chain);
724
725 /* and remove it from the data structures */
726 osm_node_remove_from_relation(appdata->osm, touchnode);
727 osm_node_delete(appdata->osm, &appdata->icon, touchnode, FALSE, TRUE);
728
729 /* and open dialog to resolve tag collisions if necessary */
730 if(conflict)
731 messagef(GTK_WIDGET(appdata->window), _("Node tag conflict"),
732 _("The resulting node contains some conflicting tags. "
733 "Please solve these."));
734
735 /* check whether this will also join two ways */
736 printf(" checking if node is end of way\n");
737 guint ways2join_cnt = 0;
738 way_t *ways2join[2] = { NULL, NULL };
739 way = appdata->osm->way;
740 while(way) {
741 if(osm_way_ends_with_node(way, node)) {
742 if(ways2join_cnt < 2)
743 ways2join[ways2join_cnt] = way;
744
745 printf(" way #"ITEM_ID_FORMAT" ends with this node\n", way->id);
746 ways2join_cnt++;
747 }
748 way = way->next;
749 }
750
751 if(ways2join_cnt > 2) {
752 messagef(GTK_WIDGET(appdata->window), _("Too many ways to join"),
753 _("More than two ways now end on this node. Joining more "
754 "than two ways is not yet implemented, sorry"));
755
756 } else if(ways2join_cnt == 2) {
757 if(yes_no_f(GTK_WIDGET(appdata->window),
758 appdata, MISC_AGAIN_ID_JOIN_WAYS, 0,
759 _("Join ways?"),
760 _("Do you want to join the dragged way with the one "
761 "you dropped it on?"))) {
762
763 printf(" about to join ways #" ITEM_ID_FORMAT " and #"
764 ITEM_ID_FORMAT "\n", ways2join[0]->id, ways2join[1]->id);
765
766 /* way[1] gets destroyed and attached to way[0] */
767 /* so check if way[1] is selected and exchainge ways then */
768 /* so that way may stay selected */
769 if((map->selected.object.type == WAY) &&
770 (map->selected.object.way == ways2join[1])) {
771 printf(" swapping ways to keep selected one alive\n");
772 way_t *tmp = ways2join[1];
773 ways2join[1] = ways2join[0];
774 ways2join[0] = tmp;
775 }
776
777 /* take all nodes from way[1] and append them to way[0] */
778 /* check if we have to append or prepend to way[0] */
779 gboolean reverse = FALSE;
780 if(ways2join[0]->node_chain->node == node) {
781 /* make "prepend" to be "append" by reversing way[0] */
782 printf(" target prepend -> reverse\n");
783 reverse = TRUE;
784 osm_way_reverse(ways2join[0]);
785 }
786
787 /* verify the common node is last in the target way */
788 node_chain_t *chain = ways2join[0]->node_chain;
789 while(chain->next) chain = chain->next;
790 g_assert(chain->node == node);
791 g_assert(!chain->next);
792
793 /* common node must be first in the chain to attach */
794 if(ways2join[1]->node_chain->node != node) {
795 printf(" source reverse\n");
796 osm_way_reverse(ways2join[1]);
797 }
798
799 /* verify the common node is first in the source way */
800 g_assert(ways2join[1]->node_chain->node == node);
801
802 /* finally append source chain to target */
803 g_assert(!chain->next);
804 chain->next = ways2join[1]->node_chain->next;
805
806 ways2join[1]->node_chain->next = NULL;
807
808 /* transfer tags from touchnode to node */
809
810 /* ---------- transfer tags from way[1] to way[0] ----------- */
811 gboolean conflict =
812 combine_tags(&ways2join[0]->tag, ways2join[1]->tag);
813 ways2join[1]->tag = NULL;
814
815 /* ---- transfer relation membership from way[1] to way[0] ----- */
816 relation_chain_t *rchain =
817 osm_way_to_relation(appdata->osm, ways2join[1]);
818
819 while(rchain) {
820 relation_chain_t *next = rchain->next;
821 printf("way[1] is part of relation #"ITEM_ID_FORMAT"\n",
822 rchain->relation->id);
823
824 /* make way[0] member of the same relation */
825
826 /* walk member chain. save role of way[1] if its being found. */
827 /* end search either at end of chain or if way[0] was foind */
828 /* as it's already a member of that relation */
829 member_t **member = &rchain->relation->member;
830 char *role = NULL;
831 while(*member &&
832 !(((*member)->object.type == WAY) &&
833 ((*member)->object.way == ways2join[0]))) {
834
835 /* save role of way[1] */
836 if(((*member)->object.type == WAY) &&
837 ((*member)->object.way == ways2join[0]))
838 role = (*member)->role;
839
840 member = &(*member)->next;
841 }
842
843 if(*member)
844 printf(" both ways were members of this relation\n");
845 else {
846 printf(" adding way[0] to relation\n");
847 *member = g_new0(member_t, 1);
848 (*member)->object.type = WAY;
849 (*member)->object.way = ways2join[0];
850 if(role) (*member)->role = g_strdup(role);
851 member = &(*member)->next;
852
853 rchain->relation->flags |= OSM_FLAG_DIRTY;
854 }
855
856 g_free(rchain);
857 rchain = next;
858 }
859
860
861 /* and open dialog to resolve tag collisions if necessary */
862 if(conflict)
863 messagef(GTK_WIDGET(appdata->window), _("Way tag conflict"),
864 _("The resulting way contains some conflicting tags. "
865 "Please solve these."));
866
867 ways2join[0]->flags |= OSM_FLAG_DIRTY;
868 map_way_delete(appdata, ways2join[1]);
869 }
870 }
871 }
872 }
873
874 /* the node either wasn't dropped into another one (touchnode) or */
875 /* the user didn't want to join the nodes */
876 if(!joined_with_touchnode) {
877
878 /* finally update dragged nodes position */
879
880 /* convert mouse position to canvas (world) position */
881 gint x, y;
882 canvas_window2world(map->canvas, ex, ey, &x, &y);
883 if(!osm_position_within_bounds(appdata->osm, x, y)) {
884 map_outside_error(appdata);
885 return;
886 }
887
888 node->lpos.x = x;
889 node->lpos.y = y;
890
891 /* convert screen position to lat/lon */
892 lpos2pos(osm->bounds, &node->lpos, &node->pos);
893
894 /* convert pos back to lpos to see rounding errors */
895 pos2lpos(osm->bounds, &node->pos, &node->lpos);
896
897 printf(" now at %d %d (%f %f)\n",
898 node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon);
899 }
900
901 /* now update the visual representation of the node */
902
903 map_item_chain_destroy(&node->map_item_chain);
904 map_node_draw(map, node);
905
906 /* visually update ways, node is part of */
907 way_t *way = osm->way;
908 while(way) {
909 if(osm_node_in_way(way, node)) {
910 printf(" node is part of way #"ITEM_ID_FORMAT", redraw!\n", way->id);
911
912 /* remove prior version of this way */
913 map_item_chain_destroy(&way->map_item_chain);
914
915 /* draw current way */
916 josm_elemstyles_colorize_way(map->style, way);
917 map_way_draw(map, way);
918 }
919
920 way = way->next;
921 }
922
923 /* and mark the node as dirty */
924 node->flags |= OSM_FLAG_DIRTY;
925
926 /* update highlight */
927 map_highlight_refresh(appdata);
928 }
929
930 /* -------------------------- way_reverse ----------------------- */
931
932 /* called from the "reverse" icon */
933 void map_edit_way_reverse(appdata_t *appdata) {
934 /* work on local copy since de-selecting destroys the selection */
935 map_item_t item = appdata->map->selected;
936
937 /* deleting the selected item de-selects it ... */
938 map_item_deselect(appdata);
939
940 g_assert(item.object.type == WAY);
941
942 osm_way_reverse(item.object.way);
943 guint n_tags_flipped =
944 osm_way_reverse_direction_sensitive_tags(item.object.way);
945 guint n_roles_flipped =
946 osm_way_reverse_direction_sensitive_roles(appdata->osm, item.object.way);
947
948 item.object.way->flags |= OSM_FLAG_DIRTY;
949 map_way_select(appdata, item.object.way);
950
951 // Flash a message about any side-effects
952 char *msg = NULL;
953 if (n_tags_flipped && !n_roles_flipped) {
954 msg = g_strdup_printf(ngettext("%d tag updated", "%d tags updated",
955 n_tags_flipped),
956 n_tags_flipped);
957 }
958 else if (!n_tags_flipped && n_roles_flipped) {
959 msg = g_strdup_printf(ngettext("%d relation updated",
960 "%d relations updated",
961 n_roles_flipped),
962 n_roles_flipped);
963 }
964 else if (n_tags_flipped && n_roles_flipped) {
965 char *msg1 = g_strdup_printf(ngettext("%d tag", "%d tags",
966 n_tags_flipped),
967 n_tags_flipped);
968 char *msg2 = g_strdup_printf(ngettext("%d relation", "%d relations",
969 n_roles_flipped),
970 n_roles_flipped);
971 msg = g_strdup_printf(_("%s & %s updated"), msg1, msg2);
972 g_free(msg1);
973 g_free(msg2);
974 }
975 if (msg) {
976 banner_show_info(appdata, msg);
977 g_free(msg);
978 }
979 }
980
981 // vim:et:ts=8:sw=2:sts=2:ai