Funkcjonalność 'undo' w aplikacji gis-owej

0

Chciałbym się poradzić odnośnie sposobu rozwiązania problemu z tematu. Rozwijam aplikację gis-ową w której użytkownicy jako jeden z problemów zgłaszali brak funkcji cofnij. W uproszczeniu aplikacja zawiera mapy dróg, które można modyfikować na wiele sposobów, wyznaczać najkrótsze drogi itp. Każda droga czy każdy jej odcinek ma swoje atrybuty jak: rodzaj nawierzchni, długość, ilość pasów itp. Dokonując zmian można zmienić parametry w jednym odcinku, w kilku zaznaczonych, we wszystkich. Ze względu na mnogość opcji nie wiem za bardzo jak zabrać się za opcję cofania operacji. Standardowo w aplikacjach opcja 'cofnij' zawiera historię operacji i wykonuje operacje przeciwne aby powrócić do poprzedniego stanu. Tutaj jest to chyba bardziej skomplikowane. Załóżmy że są 3 odcinku drogi: leśna, autostrada, wojewódzka. User zmienia sobie typ wszystkich dróg na drogę lokalną. Zapis historii operacji raczej nie pomoże bo każdy 'obiekt' wraca do innego stanu. Zmiany mogą obejmować zmiany różnych parametrów. Dochodzi do tego dodawanie i usuwanie 'obiektów' (dróg i innych elementów warstw gis-owych). Moje przemyślenia są następujące.

  1. Trzymać w pamięci całe kopie zmienianych elementów i wracać do poprzednich stanów (może zajmować sporo pamięci, duże projekty zajmują do 500 MB a dodając jakieś operacje do pamięci może się to sporo rozrosnąć).
  2. Tworzyć kopie elementów na dysku (nie wiem czy jest to możliwe bo elementy są zapisywane w plikach poszczególnych warstw, wszystkie elementy na danej warstwie w jednym pliku)
    #Stworzyć coś na wzór historii operacje w bazie danych gdzie jako blob lub bliżej nie określona struktura będzie trzymała obiekt przez zmianą (właśnie nie wiem jak miałaby wyglądać struktura takiej bazy).
    Czy ktoś miał doświadczenia z operacją cofania na złożonych strukturach i mógłbym podpowiedzieć jak się do tego zabrać. Może są jakieś inne opcje niż to co wymieniłem. W Autocadzie bardzo fajnie działa tak opcja i chce stworzyć coś na ten wzór. Będę wdzięczny za pomoc.
0

Poczytaj o wzorcach projektowych: command i memento one powinny naprowadzić cię na rozwiązanie. Niestety sposób ich implementacji, czyli: co, jak i gdzie zapamiętywać wymaga dokładniejszej znajomości architektury programu. Może ktoś bardziej doświadczony będzie mógł precyzyjniej odpowiedzieć.

0

Jeśli korzystasz z pakietu komponentów TatukGIS to jak mnie pamięć nie myli jest tam opcja zapamiętywania ilości kroków umożliwiających użytkownikowi wykonanie opcji cofnij w edytorze.

0

Dzięki za odpowiedzi.

Czytałem wcześniej o command i memento ale nie do końca jestem przekonany że zadziała to w każdym przypadku mojego problemu. Chodzi mi o dodawanie i usuwanie 'obiektów' danej warstwy. Co miałbym zapisać jako poprzedni stan przed usunięciem 'obiektu' (tutaj drogi na mapie) i po jego usunięciu? Stan po usunięciu jest raczej trudny do opisania jeśli miałbym porównywać 2 'obiekty'. Z undo wiąże się jeszcze redo. Czyli undo trzymałoby w pamięci cały mój obiekt a redo polecenie do usunięcia konkretnego obiektu? Piszę skrótowo. No i poprzednio opisany przykład z edycją kilku 'obiektów'. Edycja kilku obiektów na raz jest jednym poleceniem które zmienia wszystko wg. jednego schematu. Ale powrót do stanu poprzedniego może być dla każdego obiektu zupełnie inny. Wtedy musiałbym rozróżniać jeszcze takie przypadki kiedy edytuję pojedynczy element a kiedy grupę i inaczej zapisuję ich stany?
Interesuje mnie tutaj rzecz o której wspomniałem czyli sposób a raczej miejsce zapisu poprzednich stanów. Czy ze względu na efektywność programu i zajęte miejsce w pamięci nie lepiej byłoby zrobić tego jako pozycje w bazie dany? Dla porównania historia edycji plików pas w Delphi zapisywana w oddzielnych kopiach. Boje się czy przy wielu zmianach edycyjnych rozmiar zajmowanej pamięci nie przerośnie wielokrotnie pamięci zajmowanej przez plik bez edycji. Efektywność może bardziej stoi za rozwiązaniem w pamięci skoro po zamknięciu projektu i tak historia zmian powinna być kasowana.

Tak, korzystam z Tatuka. Niestety undo działa tylko w edycji czy po zatwierdzeniu zmian nie mam możliwości ich cofnięcia czyli mogę cofnąć kilka ruchów podczas rysowania ulicy ale kiedy jest narysowana to nic już nie zrobię.

TUndoAction nie słyszałem. Widzę że jest jakiś opis ale po niemiecku... No i raczej działa to ze standardowymi komponentami Delphi a nie dodatkowymi.

0

Na dobry początek możesz zaimplementować „undo dla ubogich”, czyli obejmujące tylko ostatnią operację. To powinno być łatwe.
Potem rozszerzyć funkcjonalność o cofanie kilku czy kilkunastu operacji (zależnie jak pamięciochłonne się to okaże). To już większości użytkowników wystarczy, i może się okazać że pełne undo skalowalne do setek czy tysięcy operacji nie będzie w ogóle potrzebne, podobnie jak głębokie przemyślenia jak to zrobić..

Zacznij od małego.

0
Clarc napisał(a):

Dzięki za odpowiedzi.

Czytałem wcześniej o command i memento ale nie do końca jestem przekonany że zadziała to w każdym przypadku mojego problemu.

Oczywiście, ze zadziała. Pamiętaj, że to jest tylko wzorzec, a nie gotowa recepta na Twoje problemy.

Chodzi mi o dodawanie i usuwanie 'obiektów' danej warstwy. Co miałbym zapisać jako poprzedni stan przed usunięciem 'obiektu' (tutaj drogi na mapie) i po jego usunięciu?

Oczywiście musisz zapamiętać stan obiektu (a więc wartości wszystkich istotnych właściwości obiektu z punktu widzenia danej akcji - tu rysowania).

Stan po usunięciu jest raczej trudny do opisania jeśli miałbym porównywać 2 'obiekty'. Z undo wiąże się jeszcze redo. Czyli undo trzymałoby w pamięci cały mój obiekt a redo polecenie do usunięcia konkretnego obiektu?

Nie obiekt jako taki, tylko jego stan.

Piszę skrótowo. No i poprzednio opisany przykład z edycją kilku 'obiektów'. Edycja kilku obiektów na raz jest jednym poleceniem które zmienia wszystko wg. jednego schematu. Ale powrót do stanu poprzedniego może być dla każdego obiektu zupełnie inny. Wtedy musiałbym rozróżniać jeszcze takie przypadki kiedy edytuję pojedynczy element a kiedy grupę i inaczej zapisuję ich stany?

To zależy od tego, czy operację wykonaną na grupie obiektów chcesz cofnąć za jednym razem czy dla każdego z obiektu z osobna.
Ale raczej tak, powinno się w takim przypadku grupować zapamiętanie stanów wielu obiektów. Jak uda Ci się dla jednego, to dla wielu będzie pestka.

Interesuje mnie tutaj rzecz o której wspomniałem czyli sposób a raczej miejsce zapisu poprzednich stanów. Czy ze względu na efektywność programu i zajęte miejsce w pamięci nie lepiej byłoby zrobić tego jako pozycje w bazie dany?

Bez znaczenia, pamiętaj że zapamiętujesz stan obiektu (np. jego reprezentację wektorową) a nie samą bitmapę. To nie zużyje dużo pamięci.

Dla porównania historia edycji plików pas w Delphi zapisywana w oddzielnych kopiach.

Bo tak jest najprościej, ale nie znaczy wcale że najlepiej.

Boje się czy przy wielu zmianach edycyjnych rozmiar zajmowanej pamięci nie przerośnie wielokrotnie pamięci zajmowanej przez plik bez edycji. Efektywność może bardziej stoi za rozwiązaniem w pamięci skoro po zamknięciu projektu i tak historia zmian powinna być kasowana.

Tak, korzystam z Tatuka. Niestety undo działa tylko w edycji czy po zatwierdzeniu zmian nie mam możliwości ich cofnięcia czyli mogę cofnąć kilka ruchów podczas rysowania ulicy ale kiedy jest narysowana to nic już nie zrobię.

Zapytaj producenta, może zrobią - kiedyś.
A jak nie zrobią to zawsze możesz zerknąć w źródła.

TUndoAction nie słyszałem. Widzę że jest jakiś opis ale po niemiecku... No i raczej działa to ze standardowymi komponentami Delphi a nie dodatkowymi.

Jest taki pies, jak akcja TEditUndo - ale to zupełnie nie to, co chcesz uzyskać...

0

Ja bym do tego podszedł tak jak Wielki Pomidor napisał, czyli nie zapamiętywał całości tylko same stany umożliwiające cofnięcie zmian.

Zakładając, że masz zdefiniowany zestaw operacji i operacji odwrotnych to tworzysz sobie coś w rodzaju stosu, na którym trzymasz operacje przeciwną do wykonanej. Do tego parametry umożliwiające poprawne wykonanie operacji przeciwnej np. poprzedni stan obiektu.

Biorąc za przykład obiekt drogi. Rozpatrzmy trzy sytuacje i ich cofnięcie:

  1. operacja tworzenia nowej drogi - na stos odkładasz operację odwrotną, czyli usunięcie i referencję do stworzonego obiektu. W tym przypadku żadne inne parametry tworzonej drogi nie są potrzebne, bo cofnięcie usuwa obiekt.
  2. operacja usuwania istniejącej drogi - na początku ukrywasz obiekt i oznaczasz go flagą - "do usunięcia", na stos odkładasz operację odwrotną, czyli pokazanie obiektu i czyszczenie flagi "do usunięcia". Rzeczywiste usuwanie obiektu następuje po jakimś "czasie" np. jeśli założysz, że można cofnąć zmiany maksymalnie 15 razy, to gdy usuwanie trafi na 16 pozycję obiekt jest niszczony i 16 pozycja stosu jest usuwana.
  3. operacja zmiana typu nawierzchni - na stos odkładasz operację odwrotną, czyli zmianę typu nawierzchni i poprzedni typ nawierzchni.
    itd.

Oczywiście ten opis jest bardzo pobieżny i niekoniecznie będzie poprawny (a nawet wykonalny) w twoim przypadku, bo tak jak pisałem wyżej potrzebne jest dużo więcej informacji o architekturze programu.

0

Dzięki za wszystkie odpowiedzi. Na razie jestem zawalony robotą ale w wolnym czasie zacznę po kolei od małych zmian i zobaczę co z tego wyjdzie. Najpierw jeden krok do tyłu a później w miarę możliwości będę rozbudowywał. Jestem w dobrej sytuacji ponieważ mam zamiar przepisać kod od nowa więc jest spore pole do wprowadzania zmian od nowa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1