Contents of /src/map_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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