Parent Directory | Revision Log
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 |