Parent Directory | Revision Log
initial line mod support and some small fixes
1 | harbaum | 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 | harbaum | 15 | if(canvas_item_get_segment(item->item, nx, ny) >= 0) |
400 | harbaum | 1 | 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 | harbaum | 15 | map_item_t *item = map_real_item_at(map, x, y); |
407 | harbaum | 1 | 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 | harbaum | 15 | map_item_t *item = map_real_item_at(map, x, y); |
490 | harbaum | 1 | 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 | achadwick | 6 | _("More than two ways now end on this node. Joining more " |
744 | harbaum | 1 | "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 |