Both sides previous revision
Poprzednia wersja
Nowa wersja
|
Poprzednia wersja
|
pl:dydaktyka:ztb:2011:projekty:przystanki:start [2011/09/18 15:21] ztb2011 |
pl:dydaktyka:ztb:2011:projekty:przystanki:start [2019/06/27 15:50] (aktualna) |
===== Data Flow Diagram - poziom zerowy ===== | ===== Data Flow Diagram - poziom zerowy ===== |
| |
{{:pl:dydaktyka:ztb:2011:projekty:przystanki:dfd0.jpg|}} | {{:pl:dydaktyka:ztb:2011:projekty:przystanki:dfd01.jpg|}} |
| |
===== Data Flow Diagram - poziom pierwszy ===== | ===== Data Flow Diagram - poziom pierwszy ===== |
| |
{{:pl:dydaktyka:ztb:2011:projekty:przystanki:dfd1.jpg|}} | {{:pl:dydaktyka:ztb:2011:projekty:przystanki:dfd11.jpg|}} |
| |
===== Data Flow Diagram - poziom drugi ===== | |
| |
{{:pl:dydaktyka:ztb:2011:projekty:przystanki:dfd2.jpg|}} | |
| |
====== 1.7 Wybór encji (obiektów) i ich atrybutów ====== | ====== 1.7 Wybór encji (obiektów) i ich atrybutów ====== |
| |
| |
Connections | - Connections |
- stop_id (int) FK (references Stops(stop _id)) | - stop_id (int) FK (references Stops(stop _id)) |
- line_id (int) FK (references Lines(line _id)) | - line_id (int) FK (references Lines(line _id)) |
- legend (text) | - legend (text) |
- order (int) | - order (int) |
| |
| |
====== 1.8 Entity-Relationship Diagram ====== | ====== 1.8 Entity-Relationship Diagram ====== |
====== 1.9 State Transition Diagram ====== | ====== 1.9 State Transition Diagram ====== |
| |
{{:pl:dydaktyka:ztb:2011:projekty:przystanki:std.jpg|}} | {{:pl:dydaktyka:ztb:2011:projekty:przystanki:std2.jpg|}} |
| |
| ====== 1.9 Wzorce projektowe ====== |
| |
| Poniżej opisane zostają wzorce, które zostały użyte projekcie w celu zoptymalizowania jej działania, głównie pod kątem bazy danych i operacji na niej. |
| |
| ===== 1.9.1 Leniwe inicjowanie ===== |
| |
| Wzorzec ten polega jak największym opóźnieniu utworzeniu obiektów aż do momentu zapotrzebowania na dany obiekt. Ma to na celu ograniczenie liczby zapytań kierowanych do bazy danych co powoduje jej odciążenie i zmniejszenie zużycia zasobów co przekłada się na zminimalizowanie zapotrzebowania na energie co w przypadku aplikacji mobilnych jest priorytetową sprawą. |
| W projekcie potrzebne linie i przystanki tworzone są w 2 przypadkach |
| - gdy musimy je wyświetlić na mapie |
| - gdy wyszukujemy je za pomocą wyszukiwarki |
| Zatem żeby odciążyć bazę obiekty opakowujące przystanki i linie są wywoływane tylko wtedy gdy znajdują się one na ekranie mapy bądź zaczynają się na daną literę bądź cyfrę wpisaną w wyszukiwarce. Po zainicjalizowaniu przechowywane są na liście stworzonych obiektów. Gdy zajdzie potrzeba ponownego ich wykorzystania program sprawdzając listę zobaczy, że obiekty były już utworzone i nie wywoła zapytania do bazy co odciąży zasoby i przyspieszy aplikację. |
| |
| ===== 1.9.2 Mapowanie danych ===== |
| |
| Z reguły w przypadku użycia wzorca leniwej inicjalizacji korzysta się również z drugiego wzorca - mapowania danych. Opisuje on tabele zawarte w bazie danych w postaci obiektów języka Java prezentując odpowiednie wiersze w postaci pól. Dodatkowo klasy te zawierają w sobie pomocnicze metody do przeszukiwania bazy z poziomu encji takie jak wyszukiwanie przystanków dla danego rejonu bądź linii na daną literę alfabetu. Przez zastosowanie tego wzorca ułatwiamy programiście dostęp do danych. |
| |
| ===== 1.9.2 Obiekt zapytania ===== |
| |
| Ostatnim użyty przez nas wzorzec naturalnie wypływa z używania technologii androidowych. Ponieważ bazę i zapytania do niej konstruujemy za pomocą DatabaseAdaptera to programista nie jest zmuszony do bezpośredniego wpisywania kwerend SQLowych, a używa funkcji w języku Java, które je obudowują. Przyczynia się to do zwiększenia przejrzystości kodu i łatwiejszego rozszerzania aplikacji. |
| |
| |
| ====== 2. Projekt logiczny ====== |
| |
| ====== 2.1 Projektowanie tabel, kluczy, kluczy obcych, powiązań między tabelami, indeksów, etc. w oparciu o zdefiniowany diagram ERD ====== |
| |
| <code> |
| // STOPS |
| public static final String PK_KEY_STOPID = "stop_id"; |
| public static final String STOPID_OPTIONS = "INTEGER PRIMARY KEY AUTOINCREMENT"; |
| public static final String KEY_LOCALISATIONX = "localisationX"; |
| public static final String LOCALISATIONX_OPTIONS = "REAL NOT NULL"; |
| public static final String KEY_LOCALISATIONY = "localisationy"; |
| public static final String LOCALISATIONY_OPTIONS = "REAL NOT NULL"; |
| public static final String KEY_NAME = "name"; |
| public static final String NAME_OPTIONS = "TEXT NOT NULL"; |
| |
| // LINES |
| public static final String PK_KEY_LINEID = "line_id"; |
| public static final String LINEID_OPTIONS = "INTEGER PRIMARY KEY AUTOINCREMENT"; |
| public static final String KEY_NUMBER = "number"; |
| public static final String NUMBER_OPTIONS = "INTEGER NOT NULL"; |
| public static final String FK_KEY_FROMID = "from_id"; |
| public static final String FROMID_OPTIONS = "INTEGER NOT NULL"; |
| public static final String FK_KEY_DESTINATIONID = "destination_id"; |
| public static final String DESTINATIONID_OPTIONS = "INTEGER NOT NULL"; |
| |
| // CONNECTIONS |
| public static final String FK_KEY_STOPID = "stop_id"; |
| public static final String STOPID_FK_OPTIONS = "INTEGER NOT NULL"; |
| public static final String FK_KEY_LINEID = "line_id"; |
| public static final String LINEID_FK_OPTIONS = "INTEGER NOT NULL"; |
| public static final String KEY_ORDERID = "order_id"; |
| public static final String ORDERID_OPTIONS = "INTEGER NOT NULL"; |
| public static final String KEY_DAY = "day"; |
| public static final String DAY_OPTIONS = "TEXT NOT NULL"; |
| public static final String KEY_HOUR = "hour"; |
| public static final String HOUR_OPTIONS = "INTEGER NOT NULL"; |
| public static final String KEY_MINUTE = "minute"; |
| public static final String MINUTE_OPTIONS = "INTEGER NOT NULL"; |
| public static final String KEY_LEGEND = "legend"; |
| public static final String LEGEND_OPTIONS = "TEXT"; |
| |
| // CREATE STOPS |
| private static final String CREATE_STOPS = "create table " + TABLE_STOPS |
| + " (" + PK_KEY_STOPID + " " + STOPID_OPTIONS + ", " |
| + KEY_LOCALISATIONX + " " + LOCALISATIONX_OPTIONS + ", " |
| + KEY_LOCALISATIONY + " " + LOCALISATIONY_OPTIONS + ", " + KEY_NAME |
| + " " + NAME_OPTIONS + ");"; |
| |
| // CREATE LINES |
| private static final String CREATE_LINES = "create table " + TABLE_LINES |
| + " (" + PK_KEY_LINEID + " " + LINEID_OPTIONS + ", " + KEY_NUMBER |
| + " " + NUMBER_OPTIONS + ", " + FK_KEY_FROMID + " " |
| + FROMID_OPTIONS + ", " + FK_KEY_DESTINATIONID + " " |
| + DESTINATIONID_OPTIONS + ", " + "FOREIGN KEY(" + FK_KEY_FROMID |
| + ") REFERENCES " + TABLE_STOPS + "(" + PK_KEY_STOPID + "), " |
| + "FOREIGN KEY(" + FK_KEY_DESTINATIONID + ") REFERENCES " |
| + TABLE_STOPS + "(" + PK_KEY_STOPID + ")" + ");"; |
| |
| // CREATE CONNECTIONS |
| private static final String CREATE_CONNECTIONS = "create table " |
| + TABLE_CONNECTIONS + " (" + FK_KEY_STOPID + " " |
| + STOPID_FK_OPTIONS + ", " + FK_KEY_LINEID + " " |
| + LINEID_FK_OPTIONS + ", " + KEY_ORDERID + " " + ORDERID_OPTIONS |
| + ", " + KEY_DAY + " " + DAY_OPTIONS + ", " + KEY_HOUR + " " |
| + HOUR_OPTIONS + ", " + KEY_MINUTE + " " + MINUTE_OPTIONS + ", " |
| + KEY_LEGEND + " " + LEGEND_OPTIONS + ", " + "FOREIGN KEY(" |
| + FK_KEY_STOPID + ") REFERENCES " + TABLE_STOPS + "(" |
| + PK_KEY_STOPID + "), " + "FOREIGN KEY(" + FK_KEY_LINEID |
| + ") REFERENCES " + TABLE_LINES + "(" + PK_KEY_LINEID + ")" + ");"; |
| </code> |
| |
| W języku SQL kwerendy te wyglądją następująco: |
| |
| <code> |
| CREATE TABLE stops ( |
| stop_id INTEGER PRIMARY KEY AUTOINCREMENT, |
| localisationX REAL NOT NULL, localisationy REAL NOT NULL, |
| name TEXT NOT NULL); |
| |
| CREATE TABLE lines ( |
| line_id INTEGER PRIMARY KEY AUTOINCREMENT, |
| number INTEGER NOT NULL, from INTEGER NOT NULL, |
| destination INTEGER NOT NULL, |
| FOREIGN KEY(from) REFERENCES stops(stop_id), |
| FOREIGN KEY(destination) REFERENCES stops(stop_id)); |
| |
| CREATE TABLE connections ( |
| stop_id INTEGER NOT NULL, |
| line_id INTEGER NOT NULL, |
| order_id INTEGER NOT NULL, |
| day TEXT NOT NULL, hour INTEGER NOT NULL, |
| minute INTEGER NOT NULL, legend TEXT, |
| FOREIGN KEY(stop_id) REFERENCES stops(stop_id), |
| FOREIGN KEY(line_id) REFERENCES lines(line_id)); |
| </code> |
| |
| ====== 2.2 Słownik pól bazy danych ====== |
| |
| - Stops |
| - stop_id (int) PK - unikalny identyfikator przystanka |
| - localisationX (real) - szerokość geograficzna przystanka |
| - localisationY (real) - długość geograficzna przystanka |
| - name (text) - nazwa przystanka |
| |
| |
| - Lines |
| - line_id (int) PK - unikalny identyfikator linii |
| - number (int) - numer linii |
| - from_id (int) FK (references Przystanki(stop _id)) - przystanek źródłowy linii |
| - destination_id (int) FK (references Przystanki(stop _id)) - przystanek docelowy linii |
| |
| |
| - Connections |
| - stop_id (int) FK (references Stops(stop _id)) - referencja do identyfikatora przystanka |
| - line_id (int) FK (references Lines(line _id)) - referencja do identyfikatora linii |
| - day (text) - rodzaj dnia tygodnia dla tabel czasowych MPK |
| - hour (int) - godzina dla poszczególnego elementu tabel czasowych MPK |
| - minute (int) - minuta dla poszczególnego elementu tabel czasowych MPK |
| - legend (text) - dodatkowe informacje dla poszczególnego elementu tabel czasowych MPK |
| - order (int) - kolejność przejazdu przez przystanki danej linii |
| |
| ====== 2.3 Analiza zależności funkcyjnych i normalizacja tabel ====== |
| |
| Baza danych spełnia 1NF ponieważ każda składowa w każdej kropce jest atomowa i nie da się jej podzielić. |
| |
| Baza danych spełnia 2NF ponieważ spełnia 1NF oraz każdy element jest zależnie funkcyjny od kluczy poszczególnych tabeli (jedyną tabelą, która nie posiada klucza głównego jest tabela connections, jest ona tabelą asocjacyjną i nie posiada niekluczowych elementów). |
| |
| Baza danych spełnie 3NF ponieważ spełnia 2NF oraz każdy atrybut jest bezpośrednio zależny od klucza głównego w poszczególnych tabelach. |
| |
| ====== 2.4 Projektowanie operacji na danych ====== |
| |
| W projekcie operacje na danych przeprowadzane są dwupoziomowo. Budowa bezpośrednich zapytań jest funkcjonalnością klasy DatabaseAdapter natomiast wstępne formowanie argumentów jest zadaniem odpowiednich klas obudowujących - Stop, Line i Connection. |
| |
| ===== 2.4.1 Projektowanie operacji na danych z poziomu Database Adaptera ===== |
| |
| <code> |
| public Cursor getAllStops() { |
| String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, |
| KEY_LOCALISATIONY, KEY_NAME }; |
| return _database.query(TABLE_STOPS, columns, null, null, null, null, |
| null); |
| } |
| |
| public Cursor getAllLines() { |
| String[] columns = { PK_KEY_LINEID, KEY_NUMBER, FK_KEY_FROMID, |
| FK_KEY_DESTINATIONID }; |
| return _database.query(TABLE_LINES, columns, null, null, null, null, |
| null); |
| } |
| |
| public Cursor getAllConnections() { |
| String[] columns = { FK_KEY_STOPID, FK_KEY_LINEID, KEY_ORDERID, |
| KEY_DAY, KEY_HOUR, KEY_MINUTE, KEY_LEGEND }; |
| return _database.query(TABLE_CONNECTIONS, columns, null, null, null, |
| null, null); |
| } |
| |
| public Cursor getStop(int stopId) { |
| String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, |
| KEY_LOCALISATIONY, KEY_NAME }; |
| String where = PK_KEY_STOPID + "=" + stopId; |
| Cursor cursor = _database.query(true, TABLE_STOPS, columns, where, |
| null, null, null, null, null); |
| return cursor; |
| } |
| |
| public Cursor getStop(String name) { |
| String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, |
| KEY_LOCALISATIONY, KEY_NAME }; |
| String where = KEY_NAME + "= '" + name + "'"; |
| Cursor cursor = _database.query(true, TABLE_STOPS, columns, where, |
| null, null, null, null, null); |
| return cursor; |
| } |
| |
| public Cursor getStopsExcept(Area area, List<Integer> stopIds) { |
| String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, |
| KEY_LOCALISATIONY, KEY_NAME }; |
| String where = area.getLeft().toString() + " < " + KEY_LOCALISATIONX |
| + " AND " + KEY_LOCALISATIONX + " < " |
| + area.getRight().toString() + " AND " |
| + area.getBottom().toString() + " < " + KEY_LOCALISATIONY |
| + " AND " + KEY_LOCALISATIONY + " < " |
| + area.getTop().toString(); |
| if (!stopIds.isEmpty()) { |
| where = where + " AND " + PK_KEY_STOPID + " NOT IN ("; |
| for (Integer id : stopIds) { |
| if (!where.endsWith("(")) { |
| where = where + ", "; |
| } |
| where = where + id.toString(); |
| } |
| where = where + ")"; |
| } |
| Cursor cursor = _database.query(true, TABLE_STOPS, columns, where, |
| null, null, null, null, null); |
| return cursor; |
| } |
| |
| public Cursor getLine(int lineId) { |
| String[] columns = { PK_KEY_LINEID, KEY_NUMBER, FK_KEY_FROMID, |
| FK_KEY_DESTINATIONID }; |
| String where = PK_KEY_LINEID + " = " + lineId; |
| Cursor cursor = _database.query(true, TABLE_LINES, columns, where, |
| null, null, null, null, null); |
| return cursor; |
| } |
| |
| public Cursor getConnection(int stopId, int lineId) { |
| String[] columns = { FK_KEY_STOPID, FK_KEY_LINEID, KEY_ORDERID, |
| KEY_DAY, KEY_HOUR, KEY_MINUTE, KEY_LEGEND }; |
| String where = FK_KEY_STOPID + "=" + stopId + " AND " + FK_KEY_LINEID |
| + "=" + lineId; |
| Cursor cursor = _database.query(TABLE_CONNECTIONS, columns, |
| where, null, null, null, null); |
| return cursor; |
| } |
| |
| public Cursor getStops(Character letter, List<Integer> ids) { |
| String[] columns = { PK_KEY_STOPID, KEY_LOCALISATIONX, |
| KEY_LOCALISATIONY, KEY_NAME }; |
| String where = KEY_NAME + " LIKE '" + Character.toUpperCase(letter) |
| + "%'"; |
| if (ids.size() > 0) { |
| where += PK_KEY_STOPID + " IS NOT IN ("; |
| for(Integer i : ids) { |
| if(! where.endsWith("(")) { |
| where += ", "; |
| } |
| where += i; |
| } |
| where += ")"; |
| } |
| return _database.query(TABLE_STOPS, columns, where, null, null, null, |
| KEY_NAME); |
| } |
| |
| public Cursor getLinesByStopId(int stopId) { |
| String[] columns = {FK_KEY_LINEID}; |
| String where = FK_KEY_STOPID + "=" + stopId; |
| return _database.query(true ,TABLE_CONNECTIONS, columns, where, null, null, null, |
| KEY_ORDERID, null); |
| } |
| |
| public Cursor getStopsByLineId(int lineId) { |
| String[] columns = {FK_KEY_STOPID}; |
| String where = FK_KEY_LINEID + "=" + lineId; |
| return _database.query(true ,TABLE_CONNECTIONS, columns, where, null, null, null, |
| KEY_ORDERID, null); |
| } |
| |
| public Cursor getLinesByLetter(Character letter) { |
| String[] columns = { PK_KEY_LINEID, KEY_NUMBER, FK_KEY_FROMID, |
| FK_KEY_DESTINATIONID }; |
| String where = "CAST(" + KEY_NUMBER + " AS TEXT) LIKE '" + letter |
| + "%'"; |
| return _database.query(TABLE_LINES, columns, where, null, null, null, |
| null); |
| } |
| </code> |
| |
| ===== 2.4.2 Projektowanie operacji na danych z poziomu Stop ===== |
| |
| <code> |
| private static Stop getStopFromCache(final int id) { |
| for (Stop stop : _stopList) { |
| if (stop.get_id() == id) |
| return stop; |
| } |
| return null; |
| } |
| |
| public static Stop getStopFromCache(String name) { |
| for (Stop stop : _stopList) { |
| if (stop.getName() == name) |
| return stop; |
| } |
| return null; |
| } |
| |
| private static Stop getStopFromCursor(final Cursor cursor) { |
| final int id = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.PK_KEY_STOPID)); |
| final Pair<Double, Double> coordinates = new Pair<Double, Double>( |
| (double) cursor.getFloat(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_LOCALISATIONY)), |
| (double) cursor.getFloat(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_LOCALISATIONX))); |
| final String name = cursor.getString(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_NAME)); |
| return new Stop(id, coordinates, name); |
| } |
| |
| private static List<Stop> changeCursorInToList(Cursor cursor) { |
| final List<Stop> stops = new ArrayList<Stop>(); |
| if (cursor != null && cursor.moveToFirst()) |
| while (true) { |
| stops.add(getStopFromCursor(cursor)); |
| if (!cursor.moveToNext()) |
| break; |
| } |
| return stops; |
| } |
| |
| private static Stop getStopFromDB(final int id, final DatabaseAdapter dba) { |
| final Cursor cursor = dba.getStop(id); |
| Stop result = null; |
| if (cursor.moveToFirst()) |
| result = getStopFromCursor(cursor); |
| cursor.close(); |
| return result; |
| } |
| |
| private static Stop getStopFromDB(final String name, |
| final DatabaseAdapter dba) { |
| final Cursor cursor = dba.getStop(name); |
| Stop result = null; |
| if (cursor.moveToFirst()) |
| result = getStopFromCursor(cursor); |
| cursor.close(); |
| return result; |
| } |
| |
| public static Stop getStop(int id, DatabaseAdapter dba) { |
| Stop stop = getStopFromCache(id); |
| if (stop == null) { |
| stop = getStopFromDB(id, dba); |
| if (stop != null) { |
| _stopList.add(stop); |
| } |
| } |
| return stop; |
| } |
| |
| public static Stop getStop(String name, DatabaseAdapter dba) { |
| Stop stop = getStopFromCache(name); |
| if (stop == null) { |
| stop = getStopFromDB(name, dba); |
| if (stop != null) { |
| _stopList.add(stop); |
| } |
| } |
| return stop; |
| } |
| |
| public static Stop getStop(final Pair<Double, Double> coordination, |
| final DatabaseAdapter dba) { |
| Double radius = BASE_RADIUS; |
| Area area = null; |
| Stop stop = null; |
| while (stop == null || radius < 5 * BASE_RADIUS) { |
| Pair<Double, Double> topLeft = new Pair<Double, Double>( |
| coordination.first + radius, coordination.second - radius); |
| Pair<Double, Double> bottomRight = new Pair<Double, Double>( |
| coordination.first - radius, coordination.second + radius); |
| area = new Rectangle(topLeft, bottomRight); |
| final List<Stop> stopsFromDB = getStops(area, dba); |
| _stopList.addAll(stopsFromDB); |
| stop = getStopFromCache(coordination, radius); |
| radius = radius + BASE_RADIUS; |
| } |
| return stop; |
| } |
| |
| private static Stop getStopFromCache( |
| final Pair<Double, Double> coordination, final Double radius) { |
| Pair<Double, Stop> result = new Pair<Double, Stop>(Double.MAX_VALUE, |
| null); |
| for (Stop stop : _stopList) { |
| Double distance = Math.sqrt(Math.pow(stop.get_coordinates().first |
| - coordination.first, 2) |
| + Math.pow(stop.get_coordinates().second |
| - coordination.second, 2)); |
| if (distance < radius && distance < result.first) { |
| result = new Pair<Double, Stop>(distance, stop); |
| } |
| } |
| return result.second; |
| } |
| |
| private static List<Stop> getStopsFromCache(final Area area) { |
| final List<Stop> stopsInArea = new ArrayList<Stop>(); |
| for (Stop stop : _stopList) { |
| if (area.isPointInArea(stop.get_coordinates())) |
| stopsInArea.add(stop); |
| } |
| return stopsInArea; |
| } |
| |
| private static List<Stop> getStopsFromCache(final String name) { |
| final List<Stop> stopsByLetter = new ArrayList<Stop>(); |
| for (Stop stop : _stopList) { |
| if (stop.getName().toUpperCase().startsWith(name.toUpperCase())) |
| stopsByLetter.add(stop); |
| } |
| return stopsByLetter; |
| } |
| |
| private static List<Stop> getStopsFromDB(final Area area, |
| final DatabaseAdapter dba) { |
| final List<Integer> except = new ArrayList<Integer>(); |
| final List<Stop> stopsInArea = getStopsFromCache(area); |
| for (Stop stop : stopsInArea) |
| except.add(stop.get_id()); |
| Cursor cursor = dba.getStopsExcept(area, except); |
| final List<Stop> result = changeCursorInToList(cursor); |
| cursor.close(); |
| return result; |
| } |
| |
| private static int getStopIdFromCursor(final Cursor cursor) { |
| return (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.FK_KEY_STOPID)); |
| } |
| |
| private static List<Stop> getStopsByLineIdFromDB(final int lineId, |
| final DatabaseAdapter dba) { |
| final List<Stop> stops = new ArrayList<Stop>(); |
| final Cursor cursor = dba.getStopsByLineId(lineId); |
| if (cursor != null && cursor.moveToFirst()) |
| while (true) { |
| Stop result = getStop(getStopIdFromCursor(cursor), dba); |
| stops.add(result); |
| if (!cursor.moveToNext()) |
| break; |
| } |
| cursor.close(); |
| return stops; |
| } |
| |
| private static List<Stop> getStopsFromDB(final Character letter, |
| final DatabaseAdapter dba) { |
| List<Integer> intsList = new ArrayList<Integer>(); |
| List<Stop> stopsList = getStopsFromCache(letter.toString().toUpperCase()); |
| for(Stop s : stopsList) { |
| intsList.add(s.get_id()); |
| } |
| Cursor cursor = dba.getStops(letter, intsList); |
| List<Stop> sl = changeCursorInToList(cursor); |
| cursor.close(); |
| return sl; |
| } |
| |
| public static List<Stop> getStops(final Area area, final DatabaseAdapter dba) { |
| if (area != null) { |
| final List<Stop> stops = getStopsFromDB(area, dba); |
| _stopList.addAll(stops); |
| } |
| return getStopsFromCache(area); |
| } |
| |
| public static List<Stop> getStops(final String name, |
| final DatabaseAdapter dba) { |
| Character letter = null; |
| if (name.length() > 0) { |
| letter = name.toUpperCase().charAt(0); |
| } |
| if (letter == null) { |
| return null; |
| } |
| if (!_letters.contains(letter)) { |
| final List<Stop> stops = getStopsFromDB(letter, dba); |
| _stopList.addAll(stops); |
| _letters.add(letter); |
| } |
| return getStopsFromCache(name); |
| } |
| |
| public static List<Stop> getStopsByLineId(int lineId, DatabaseAdapter dba) { |
| return getStopsByLineIdFromDB(lineId, dba); |
| } |
| </code> |
| |
| ===== 2.4.3 Projektowanie operacji na danych z poziomu Line ===== |
| |
| <code> |
| private static Line getLineFromCache(final int id) { |
| for (Line line : _lineList) { |
| if (line.get_id() == id) |
| return line; |
| } |
| return null; |
| } |
| |
| private static List<Line> getLinesFromCache(final String name) { |
| final List<Line> linesByLetter = new ArrayList<Line>(); |
| for (Line line : _lineList) { |
| if (new Integer(line.getNumber()).toString().toUpperCase() |
| .startsWith(name.toUpperCase())) |
| linesByLetter.add(line); |
| } |
| return linesByLetter; |
| } |
| |
| private static Line getLineFromCursor(final Cursor cursor) { |
| final int id = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.PK_KEY_LINEID)); |
| final int number = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_NUMBER)); |
| final int from = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.FK_KEY_FROMID)); |
| final int destination = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.FK_KEY_DESTINATIONID)); |
| return new Line(id, number, from, destination); |
| } |
| |
| private static int getLineIdFromCursor(final Cursor cursor) { |
| return (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.FK_KEY_LINEID)); |
| } |
| |
| private static List<Line> getLineByStopIdFromDB(final int stopId, |
| final DatabaseAdapter dba) { |
| final List<Line> lines = new ArrayList<Line>(); |
| final Cursor cursor = dba.getLinesByStopId(stopId); |
| if (cursor != null && cursor.moveToFirst()) |
| while (true) { |
| Line result = getLine(getLineIdFromCursor(cursor), dba); |
| lines.add(result); |
| if (!cursor.moveToNext()) |
| break; |
| } |
| cursor.close(); |
| return lines; |
| } |
| |
| private static Line getLineFromDB(final int id, final DatabaseAdapter dba) { |
| final Cursor cursor = dba.getLine(id); |
| Line result = null; |
| if (cursor.moveToFirst()) |
| result = getLineFromCursor(cursor); |
| cursor.close(); |
| return result; |
| } |
| |
| private static List<Line> changeCursorInToList(Cursor cursor) { |
| final List<Line> lines = new ArrayList<Line>(); |
| if (cursor != null && cursor.moveToFirst()) |
| while (true) { |
| lines.add(getLineFromCursor(cursor)); |
| if (!cursor.moveToNext()) |
| break; |
| } |
| return lines; |
| } |
| |
| private static List<Line> getLinesFromDB(final Character letter, |
| final DatabaseAdapter dba) { |
| List<Integer> intsList = new ArrayList<Integer>(); |
| List<Line> linesList = getLinesFromCache(letter.toString().toUpperCase()); |
| for(Line s : linesList) { |
| intsList.add(s.get_id()); |
| } |
| Cursor cursor = dba.getLinesByLetter(letter); |
| List<Line> ll = changeCursorInToList(cursor); |
| cursor.close(); |
| return ll; |
| } |
| |
| public static Line getLine(int id, DatabaseAdapter dba) { |
| Line line = getLineFromCache(id); |
| if (line == null) { |
| line = getLineFromDB(id, dba); |
| if (line != null) { |
| _lineList.add(line); |
| } |
| } |
| return line; |
| } |
| |
| public static List<Line> getLines(final String name, |
| final DatabaseAdapter dba) { |
| Character letter = null; |
| if (name.length() > 0) { |
| letter = name.toUpperCase().charAt(0); |
| } |
| if (letter == null) { |
| return null; |
| } |
| if (!_letters.contains(letter)) { |
| final List<Line> stops = getLinesFromDB(letter, dba); |
| _lineList.addAll(stops); |
| _letters.add(letter); |
| } |
| return getLinesFromCache(name); |
| } |
| |
| public static List<Line> getLinesByStopId(int stopId, DatabaseAdapter dba) { |
| return getLineByStopIdFromDB(stopId, dba); |
| } |
| </code> |
| |
| ===== 2.4.4 Projektowanie operacji na danych z poziomu Connection ===== |
| |
| <code> |
| private static Connection getConnectionFromCache(final int stopId, |
| final int lineId) { |
| for (Connection conn : _connectionList) { |
| if (conn.get_stopId() == stopId && conn.get_lineId() == lineId) |
| return conn; |
| } |
| return null; |
| } |
| |
| private void addTime(Pair<String, Pair<Integer, Integer>> time) { |
| if (_TimeTable.containsKey(time.first)) { |
| List<Pair<Integer, Integer>> timeList = _TimeTable.get(time.first); |
| timeList.add(time.second); |
| _TimeTable.put(time.first, timeList); |
| } else { |
| List<Pair<Integer, Integer>> timeList = new ArrayList<Pair<Integer, Integer>>(); |
| timeList.add(time.second); |
| _TimeTable.put(time.first, timeList); |
| } |
| } |
| |
| private static Connection getConnectionFromCursor(final Cursor cursor) { |
| final int stopId = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.FK_KEY_STOPID)); |
| final int lineId = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.FK_KEY_LINEID)); |
| final int orderId = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_ORDERID)); |
| final String legend = cursor.getString(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_LEGEND)); |
| return new Connection(stopId, lineId, orderId, legend); |
| } |
| |
| private static Pair<String, Pair<Integer, Integer>> getTimeFromCursor( |
| final Cursor cursor) { |
| final String day = cursor.getString(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_DAY)); |
| final int hour = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_HOUR)); |
| final int minute = (int) cursor.getLong(cursor |
| .getColumnIndex(DatabaseAdapter.KEY_MINUTE)); |
| return new Pair<String, Pair<Integer, Integer>>(day, |
| new Pair<Integer, Integer>(hour, minute)); |
| } |
| |
| private static Connection getConnectionFromDB(final int stopId, |
| final int lineId, final DatabaseAdapter dba) { |
| final Cursor cursor = dba.getConnection(stopId, lineId); |
| Connection result = null; |
| if (cursor.moveToFirst()) |
| result = getConnectionFromCursor(cursor); |
| while (true) { |
| result.addTime(getTimeFromCursor(cursor)); |
| if (!cursor.moveToNext()) |
| break; |
| } |
| cursor.close(); |
| return result; |
| } |
| |
| public static Connection getConnection(int stopId, int lineId, |
| DatabaseAdapter dba) { |
| Connection conn = getConnectionFromCache(stopId, lineId); |
| if (conn == null) { |
| conn = getConnectionFromDB(stopId, lineId, dba); |
| if (conn != null) { |
| _connectionList.add(conn); |
| } |
| } |
| return getConnectionFromDB(stopId, lineId, dba); |
| } |
| </code> |
| |
| |
| ====== 3. Raport końcowy ====== |
| |
| ====== 3.1 Implementacja bazy danych ====== |
| |
| Baza danych aplikacji mobilnej została zaimplementowana z wykorzystaniem motoru bazy SQLite. Elementy tworzące bazę, zostały zaprezentowane w projekcie logicznym. |
| |
| ====== 3.2 Interfejsy do obsługi danych ====== |
| |
| W aplikacji istnieją następujące przepływy danych przez widoku (aktywności Androida): |
| |
| - Mapa |
| - odnalezienie pobliskiego przystanku na mapie (widok mapy) |
| - wybranie odpowiadającej użytkownikowi linii (widok linii dla danego przystanka) |
| - sprawdzenie godzin odjazdów danej linii z danego przystanka (widok połączenia) |
| - Wyszukiwarka przystanków |
| - wyszukanie przystanku po nazwie (widok wyszukiwarki) |
| - wybranie odpowiadającej użytkownikowi linii (widok linii dla danego przystanka) |
| - sprawdzenie godzin odjazdów danej linii z danego przystanka (widok połączenia) |
| - Wyszukiwarka przystanków |
| - wyszukanie przystanku po numerze (widok wyszukiwarki) |
| - wybranie odpowiadającego użytkownikowi przystanku (widok przystanków dla danej linii) |
| - sprawdzenie godzin odjazdów danej linii z danego przystanka (widok połączenia) |
| |
| Każdorazowo pobierane są odpowiednie dane z bazy (przystanki, linie, połączenia) ze zminimalizowanym obciążeniem bazy danych poprzez zastosowanie interfejsu lazy load (dane inicjowane są dopiero gdy są potrzebne i są przechowywane żeby uniknąć niepotrzebnych zapytać do bazy). |
| |
| Widok mapy |
| |
| {{:pl:dydaktyka:ztb:2011:projekty:przystanki:mapa.jpg|}} |
| |
| Widok linii przy wyszukiwaniu |
| |
| {{:pl:dydaktyka:ztb:2011:projekty:przystanki:search.jpg|}} |
| |
| Widok linii dla danego przystanka |
| |
| {{:pl:dydaktyka:ztb:2011:projekty:przystanki:linie.jpg|}} |
| |
| ====== 3.3 Dalszy rozwój aplikacji mobilnej ====== |
| |
| Aplikacja aktualnie spełnia wszystkie podstawowe założenia, które założyliśmy w fazie projektowej. W przyszłości planujemy rozwój: |
| - wyznaczanie tras dojścia do najbliższych przystanków |
| - wyświetlanie czasu odjazdów dla aktualnej i zadanej pory |
| - wyświetlanie tras przejazdu danej linii |
| |
| ====== 3.4 Wnioski ====== |
| |
| Google dostarczyło nam narzędzie, które jest łatwo przyswajalne i dobrze opisane w dokumentacji wraz z licznymi przykładami użycia. Sprawiło to, że napisanie aplikacji na system Android nie sprawiło zespołowi problemów. |
| SQLite jako baza danych dla aplikacji mobilnych wydaje się odpowiednim wyborem. Nie dostarcza co prawda zbyt wielu funkcjonalności, ale jest prosta w obsłudze, a do tego lekka (co powoduje małe zużycie energii co w aplikacjach mobilnych jest kluczowe) i szybka. |
| |
| ====== 3.4 Literatura ====== |
| |
| - http://developer.android.com/index.html |
| - http://www.sqlite.org/docs.html |