Changes between Version 10 and Version 11 of AdvancedTopics


Ignore:
Timestamp:
05/20/26 13:31:18 (6 days ago)
Author:
231044
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedTopics

    v10 v11  
    9797[attachment:FlightRoutes.sql]
    9898
    99 
    100 == 3. Каде е интегриран PostGIS? ==
    101 
    102 PostGIS се протега низ повеќе компоненти на постоечкиот проект:
    103 
    104 || '''Компонента''' || '''Тип на интеграција''' ||
    105 || Табела {{{Airport}}} || Додадени се колоните {{{latitude}}}, {{{longitude}}}, {{{location}}} (geography Point) ||
    106 || Колона {{{Airport.location}}} || Просторен GIST индекс за брзи геопросторни прашалници ||
    107 || Табела {{{ScheduledFlight}}} || Колоната {{{distance}}} е ажурирана со пресметани реални дистанци ||
    108 || Тригер {{{SetDistance}}} || Автоматски пресметува дистанца при INSERT/UPDATE на !ScheduledFlight ||
    109 || Поглед {{{FlightRoutes}}} || Нов поглед кој ги претставува летовите како !LineString геометрии ||
    110 || Функција {{{FindNearestAirport}}} || Нова функција за наоѓање најблиски аеродроми ||
    111 || Функција {{{FlightAtTime}}} || Нова функција за пресметка на локација на лет во даден момент ||
    112 || Тригер {{{AwardMilePoints}}} (Фаза 4) || Сега ги пресметува миљните поени врз основа на реални растојанија ||
    113 
    114 ----
    115 
    116 == 4. Технички предуслови ==
    117 
    118 Пред да може да се користи PostGIS, екстензијата мора да биде активирана во базата на податоци:
     99За успешна визуелизација ги извршивме следните прашалници:
    119100
    120101{{{
    121102#!sql
    122 CREATE EXTENSION postgis;
     103select * from flightroutes
     104where origin = 'SKP';
    123105}}}
    124106
    125 Оваа команда ги регистрира сите PostGIS типови на податоци ({{{geography}}}, {{{geometry}}}), функции ({{{ST_Distance}}}, {{{ST_MakePoint}}}, {{{ST_LineInterpolatePoint}}} итн.) и оператори (просторниот KNN оператор {{{<->}}}) во моменталната база.
     107Летови од Скопје
    126108
    127 ----
    128 
    129 == 5. Промени на постоечките табели ==
    130 
    131 === 5.1 Додавање на колоните latitude и longitude ===
    132 
    133 Првиот чекор беше да се обогати табелата {{{Airport}}} со географски координати. Овие податоци ги имавме од оригиналниот CSV фајл ({{{airports.csv}}} од !OpenFlights), но во Фаза 2Б беа отфрлени бидејќи тогаш немавме просторна функционалност.
     109[[Image(RoutesFromSkopje.png, 800px, align=center)]]
    134110
    135111{{{
    136112#!sql
    137 ALTER TABLE airport ADD COLUMN latitude  numeric;
    138 ALTER TABLE airport ADD COLUMN longitude numeric;
     113select * from flightroutes
     114where destination = 'EWR';
    139115}}}
    140116
    141 Колоните потоа се пополнија со податоци од привремена табела {{{temp_airports}}}, спарувајќи по IATA код:
     117Летови до Newark
    142118
    143 {{{
    144 #!sql
    145 UPDATE airport a
    146 SET latitude  = ta.latitude::numeric,
    147     longitude = ta.longitude::numeric
    148 FROM (
    149     SELECT DISTINCT ON (iata) iata, latitude, longitude
    150     FROM temp_airports
    151     WHERE latitude  NOT IN ('\N','')
    152       AND longitude NOT IN ('\N','')
    153     ORDER BY iata
    154 ) ta
    155 WHERE a.code = ta.iata;
    156 }}}
     119[[Image(RoutesToNewark.png, 800px, align=center)]]
    157120
    158 Овие колони служат како „сурова" форма на координатите — лесни за читање и експортирање, но не директно искористливи во просторни прашалници.
    159121
    160 === 5.2 Додавање на колоната location (geography Point) ===
    161 
    162 Главната просторна колона е {{{location}}}, која ги обединува latitude и longitude во една PostGIS геометрија:
    163 
    164 {{{
    165 #!sql
    166 ALTER TABLE airport
    167 ADD COLUMN location geography(Point, 4326);
    168 
    169 UPDATE airport
    170 SET location = ST_MakePoint(longitude, latitude)::geography;
    171 }}}
    172 
    173 Неколку важни детали:
    174 
    175  * '''Тип {{{geography(Point, 4326)}}}.''' PostGIS нуди два главни типа на просторни податоци: {{{geometry}}} (рамна, евклидска геометрија) и {{{geography}}} (вистинска сферна геометрија на Земјата). За авиокомпаниски проект, {{{geography}}} е правилниот избор бидејќи летовите се на голема скала и кривината на Земјата е значајна — растојанијата мора да се мерат како great-circle distances, а не како евклидски растојанија.
    176  * '''SRID 4326.''' Ова е стандардот WGS 84 (World Geodetic System 1984), истиот референтен систем кој го користат GPS уредите. Со ова, координатите од нашиот CSV (кои се во WGS 84) се компатибилни со колоната без потреба од репроекција.
    177  * '''Редослед на параметри: {{{ST_MakePoint(longitude, latitude)}}}.''' PostGIS следи XY-конвенција, што значи дека longitude е X (надолжна, „хоризонтална" оска) и latitude е Y. Замена на овие два параметра е честа грешка која води до точки во погрешен дел на светот.
    178 
    179 === 5.3 Просторен GIST индекс ===
    180 
    181 За да може PostGIS брзо да изврши просторни прашалници врз милиони редици, на колоната {{{location}}} се додаде GIST индекс:
    182 
    183 {{{
    184 #!sql
    185 CREATE INDEX idx_airport_location ON airport USING GIST (location);
    186 }}}
    187 
    188 За разлика од B-tree индексите (кои се користат за подредени скаларни вредности — броеви, текстови, датуми), GIST индексите се специјализирани за повеќедимензионални податоци. Тие овозможуваат:
    189 
    190  * '''Range queries во просторот''' — „пронајди ги сите аеродроми во радиус од 500 km околу точката X"
    191  * '''Nearest-neighbor queries''' — „пронајди ги најблиските 10 аеродроми од дадена точка", преку K-nearest-neighbor операторот {{{<->}}}
    192  * '''Intersection queries''' — „пронајди ги сите рути кои поминуваат низ дадена област"
    193 
    194 Без GIST индекс, секоја таква претрага би била sequential scan низ целата табела.
    195 
    196 ----
    197 
    198 == 6. Поглед FlightRoutes ==
    199 
    200 Создаден е нов поглед кој ги претставува летовите како геометриски линии на мапата:
    201 
    202 {{{
    203 #!sql
    204 CREATE VIEW FlightRoutes (FlightID, FlightNumber, Departure, Arrival,
    205     Origin, Destination, Route, Distance) AS
    206 SELECT flight.id, flight.flightnumber,
    207        flight.departure, flight.arrival,
    208        ad.code, aa.code,
    209        ST_MakeLine(ad.location::geometry, aa.location::geometry),
    210        scheduledflight.distance
    211 FROM flight
    212 JOIN gate     AS ga ON flight.actualgatearrivalid   = ga.id
    213 JOIN gate     AS gd ON flight.actualgatedepartureid = gd.id
    214 JOIN terminal AS ta ON ta.id = ga.terminalid
    215 JOIN terminal AS td ON td.id = gd.terminalid
    216 JOIN airport  AS aa ON aa.id = ta.airportid
    217 JOIN airport  AS ad ON ad.id = td.airportid
    218 JOIN scheduledflight ON flight.scheduleid = scheduledflight.id
    219 WHERE aa.location IS NOT NULL AND ad.location IS NOT NULL;
    220 }}}
    221 
    222 '''Како функционира:'''
    223 
    224  * Функцијата {{{ST_MakeLine(departure_point, arrival_point)}}} гради {{{LineString}}} геометрија — права линија помеѓу две точки во геометрискиот простор.
    225  * Низ ланецот од JOIN-ови, погледот ги доведува аеродромите за полетување и слетување на секој лет (преку gate → terminal → airport).
    226  * Резултатот е една редица по лет, со полно име, временски печат, кодови на полетниот и слетниот аеродром, реална географска линија помеѓу нив и пресметана дистанца.
    227 
    228 '''Случаи на употреба:'''
    229 
    230  * '''Визуелизација во QGIS''' — погледот може да се вчита како просторен слој и да прикаже сите рути на мапа.
    231  * '''Мрежна анализа''' — кои аеродроми се најпрометни, кои рути се најфреквентни, која е географската покриеност на авиокомпанијата.
    232  * '''Просторни прашалници''' — на пример, „кои летови поминуваат во радиус од 200 km околу одредена точка" може да се изврши со {{{ST_DWithin}}} врз колоната {{{Route}}}.
    233 
    234 === 6.1 Скриншоти ===
    235 
    236 '''Сите аеродроми на светот:'''[[BR]]
    237 [[Image(Screenshots/World-Airports.png)]]
    238 
    239 '''Аеродроми на Балканот:'''[[BR]]
    240 [[Image(Screenshots/Balkan.png)]]
    241 
    242 '''Рути од Скопје:'''[[BR]]
    243 [[Image(Screenshots/RoutesFromSkopje.png)]]
    244 
    245 '''Рути до Newark (EWR):'''[[BR]]
    246 [[Image(Screenshots/RoutesToNewark.png)]]
    247 
    248 ----
    249 
    250 == 7. Ажурирање на дистанците во ScheduledFlight ==
    251 
    252 Една од најважните придобивки на PostGIS во овој проект е замена на лажните, синтетички дистанци со вистински географски вредности.
    253 
    254 {{{
    255 #!sql
    256 UPDATE scheduledflight AS sf
    257 SET distance = GREATEST(1, ROUND(ST_Distance(dep.location, arr.location) / 1000)::int)
    258 FROM slot AS ds
    259 JOIN gate     AS dg ON ds.gateid     = dg.id
    260 JOIN terminal AS dt ON dg.terminalid = dt.id
    261 JOIN airport  AS dep ON dep.id       = dt.airportid,
    262      slot AS asl
    263 JOIN gate     AS ag ON asl.gateid    = ag.id
    264 JOIN terminal AS at ON at.id         = ag.terminalid
    265 JOIN airport  AS arr ON arr.id       = at.airportid
    266 WHERE sf.departure = ds.id
    267   AND sf.arrival   = asl.id
    268   AND dep.location IS NOT NULL
    269   AND arr.location IS NOT NULL;
    270 }}}
    271 
    272 '''Детали за пресметката:'''
    273 
    274  * {{{ST_Distance(dep.location, arr.location)}}} враќа great-circle дистанца помеѓу две {{{geography}}} точки во метри.
    275  * Делењето со 1000 ја претвора во километри.
    276  * {{{ROUND(...)::int}}} ја кружи до цел број за да одговара на типот на колоната.
    277  * {{{GREATEST(1, ...)}}} гарантира минимална вредност од 1 km (поради CHECK ограничувањето {{{distance > 0}}}). Ова е важно за ретки случаи каде два аеродроми се толку близу што дистанцата заокружена на километри би била 0.
    278 
    279 '''Последица низ остатокот на системот:'''
    280 
    281 Постоечкиот тригер {{{AwardMilePoints}}} (од Фаза 4) ги пресметува миљните поени на патниците врз основа на {{{ScheduledFlight.Distance}}}, помножена со множител по класа (Economy: 1×, Business: 2×, First: 3×). По ова ажурирање, поените повеќе не се „измислени" — патник со First класа на лет JFK → LAX добива ~4000 km × 3 = 12,000 поени, што одговара на реално долг лет.
    282 
    283 ----
    284 
    285 == 8. Тригер SetDistance (CalculateDistance) ==
    286 
    287 За да не мора рачно да се пресметува дистанцата секогаш кога ќе се додаде нов закажан лет, имплементиран е тригер кој автоматски ја пресметува:
    288 
    289 {{{
    290 #!sql
    291 CREATE OR REPLACE FUNCTION CalculateDistance()
    292 RETURNS trigger AS $$
    293 BEGIN
    294     SELECT GREATEST(1, ROUND(ST_Distance(dep.location, arr.location) / 1000)::int)
    295     INTO NEW.distance
    296     FROM slot AS ds
    297     JOIN gate     AS dg ON ds.gateid     = dg.id
    298     JOIN terminal AS dt ON dg.terminalid = dt.id
    299     JOIN airport  AS dep ON dep.id       = dt.airportid,
    300          slot AS asl
    301     JOIN gate     AS ag ON asl.gateid    = ag.id
    302     JOIN terminal AS at ON at.id         = ag.terminalid
    303     JOIN airport  AS arr ON arr.id       = at.airportid
    304     WHERE NEW.departure = ds.id
    305       AND NEW.arrival   = asl.id
    306       AND dep.location IS NOT NULL
    307       AND arr.location IS NOT NULL;
    308 
    309     RETURN NEW;
    310 END;
    311 $$ LANGUAGE plpgsql;
    312 
    313 CREATE TRIGGER SetDistance
    314 BEFORE INSERT OR UPDATE OF departure, arrival ON scheduledflight
    315 FOR EACH ROW EXECUTE FUNCTION CalculateDistance();
    316 }}}
    317 
    318 '''Карактеристики:'''
    319 
    320  * '''{{{BEFORE INSERT OR UPDATE}}}''' — тригерот се извршува ''пред'' редицата да биде впишана/ажурирана. Тоа му овозможува да ја смени вредноста на {{{NEW.distance}}} пред таа да биде валидирана од CHECK ограничувањата.
    321  * '''{{{UPDATE OF departure, arrival}}}''' — тригерот се извршува само кога овие конкретни колони се менуваат. Доколку се ажурира некоја друга колона (на пример, {{{active}}}), тригерот не се активира — оптимизација која штеди непотребна работа.
    322  * '''{{{SELECT ... INTO NEW.distance}}}''' — наместо да враќа вредност на повикувачот, тригерот ја менува вредноста на {{{NEW}}} пред commit-ирање.
    323 
    324 '''Зошто тригер наместо generated column?''' PostgreSQL поддржува {{{GENERATED ALWAYS AS (...) STORED}}} колони, но тие не можат да користат subqueries или JOIN-ови (бидејќи мораат да бидат детерминистички и без странични ефекти). Бидејќи пресметката на дистанцата бара JOIN преку {{{slot → gate → terminal → airport}}}, мораше да се користи тригер.
    325 
    326 '''Случај на употреба:''' доколку во иднина се додадат нови закажани летови (нови дестинации, проширување на мрежата), нивната дистанца автоматски се пресметува правилно — администраторот не мора да внимава да ја внесе како посебен чекор.
    327 
    328 ----
    329 
    330 == 9. Функција FindNearestAirport ==
    331 
    332 {{{
    333 #!sql
    334 CREATE OR REPLACE FUNCTION FindNearestAirport(
    335     longitude_P numeric,
    336     latitude_P  numeric,
    337     distanceKm  int
    338 ) RETURNS TABLE (
    339     AirportCode     char(3),
    340     AirportName     text,
    341     AirportLocation geography(Point, 4326),
    342     Distance_KM     numeric
    343 ) AS $$
    344 BEGIN
    345     RETURN QUERY
    346     SELECT a.code, a.name, a.location,
    347            (ST_Distance(a.location, ST_MakePoint(longitude_P, latitude_P)::geography) / 1000)::numeric
    348     FROM airport AS a
    349     WHERE ST_DWithin(
    350         a.location,
    351         ST_MakePoint(longitude_P, latitude_P)::geography,
    352         distanceKm * 1000
    353     )
    354     ORDER BY a.location <-> ST_MakePoint(longitude_P, latitude_P)::geography;
    355 END;
    356 $$ LANGUAGE plpgsql;
    357 }}}
    358 
    359 '''Како функционира:'''
    360 
    361  * Прима три параметри: longitude и latitude на референтна точка, плус максимален радиус во километри.
    362  * Враќа табела на сите аеродроми во тој радиус, заедно со нивната локација и точното растојание.
    363  * Резултатите се сортирани по растојание — најблискиот аеродром е прв.
    364 
    365 '''Клучни PostGIS компоненти:'''
    366 
    367  * '''{{{ST_DWithin(geog_a, geog_b, distance_meters)}}}''' — враќа TRUE доколку две географски точки се на растојание помало или еднакво на дадениот број метри. Ова е првиот филтер кој го „сече" пребарувањето.
    368  * '''Множењето {{{distanceKm * 1000}}}''' ги претвора километрите во метри (PostGIS интерно работи во метри за geography).
    369  * '''KNN оператор {{{<->}}}''' — посебен оператор за nearest-neighbor сортирање. За разлика од обичен {{{ORDER BY ST_Distance(...)}}}, овој оператор го користи GIST индексот директно и е значајно побрз при големи табели.
    370 
    371 '''Случаи на употреба:'''
    372 
    373  * '''Препораки за алтернативни аеродроми''' — кога одреден лет се откажува, можеме да им предложиме на патниците блиски аеродроми (на пример, ако се откажува лет до LHR, можеме да предложиме LGW, STN, LTN).
    374  * '''Логистика на товар''' — наоѓање најблизок аеродром до магацин или дистрибутивен центар.
    375  * '''Кориснички интерфејс''' — „наоѓаш ли се близу аеродром?" функционалност за мобилна апликација.
    376 
    377 '''Демонстрација:''' наоѓање на најблиските аеродроми во радиус од 100 km околу Скопје (координати 21.62°E, 41.96°N):
    378 
    379 {{{
    380 #!sql
    381 SELECT * FROM FindNearestAirport(21.62, 41.96, 100);
    382 }}}
    383 
    384 [[Image(Screenshots/NearestAirportsSkopje.png)]]
    385 
    386 ----
    387 
    388 == 10. Функција FlightAtTime ==
    389 
    390 Оваа функција пресметува каде се наоѓа определен лет (естимативно) во даден временски момент — без потреба од посебна табела со логирани локации.
    391 
    392 {{{
    393 #!sql
    394 CREATE OR REPLACE FUNCTION FlightAtTime(
    395     FlightID_P int,
    396     Time_P     timestamp
    397 ) RETURNS TABLE (
    398     CurrentLocation geography,
    399     Route           geography
    400 ) AS $$
    401 DECLARE
    402     DepartureTime     timestamp;
    403     ArrivalTime       timestamp;
    404     DepartureLocation geography;
    405     ArrivalLocation   geography;
    406     Fraction          numeric;
    407     CurrentPosition_V geography;
    408     Route_V           geography;
    409 BEGIN
    410     SELECT flight.departure, flight.arrival, ad.location, aa.location
    411     INTO DepartureTime, ArrivalTime, DepartureLocation, ArrivalLocation
    412     FROM flight
    413     JOIN gate     AS ga ON flight.actualgatearrivalid   = ga.id
    414     JOIN gate     AS gd ON flight.actualgatedepartureid = gd.id
    415     JOIN terminal AS ta ON ta.id = ga.terminalid
    416     JOIN terminal AS td ON td.id = gd.terminalid
    417     JOIN airport  AS aa ON aa.id = ta.airportid
    418     JOIN airport  AS ad ON ad.id = td.airportid
    419     WHERE flight.id = FlightID_P;
    420 
    421     IF Time_P <= DepartureTime THEN
    422         CurrentPosition_V := DepartureLocation;
    423     ELSIF Time_P >= ArrivalTime THEN
    424         CurrentPosition_V := ArrivalLocation;
    425     ELSE
    426         Fraction := EXTRACT(epoch FROM (Time_P - DepartureTime))
    427                   / EXTRACT(epoch FROM (ArrivalTime - DepartureTime));
    428 
    429         CurrentPosition_V := ST_LineInterpolatePoint(
    430             ST_MakeLine(DepartureLocation::geometry, ArrivalLocation::geometry),
    431             Fraction
    432         )::geography;
    433     END IF;
    434 
    435     Route_V := ST_MakeLine(
    436         DepartureLocation::geometry,
    437         ArrivalLocation::geometry
    438     )::geography;
    439 
    440     RETURN QUERY SELECT CurrentPosition_V, Route_V;
    441 END;
    442 $$ LANGUAGE plpgsql;
    443 }}}
    444 
    445 '''Како функционира:'''
    446 
    447  1. '''Се извлекуваат деталите за летот''' — времето на полетување и слетување, и локациите на двата аеродрома преку познатиот ланец на JOIN-ови.
    448  2. '''Се определува моменталната локација според временскиот параметар:'''
    449    * Доколку времето е ''пред'' полетувањето, авионот сè уште е на полетниот аеродром.
    450    * Доколку времето е ''по'' слетувањето, авионот веќе пристигнал на слетниот аеродром.
    451    * Во спротивно, се пресметува {{{Fraction}}} — фракција помеѓу 0 и 1 која означува „колку процент од летот е изминат".
    452  3. '''Се користи {{{ST_LineInterpolatePoint}}}''' — функција која враќа точка на одредена фракција должина по една линија. Со fraction = 0.5, се добива средината на патот; со fraction = 0.25, една четвртина од него.
    453  4. '''Се враќа и моменталната локација и целата рута''' — корисно за визуализација (точка на мапа за авионот, плус линија која ја прикажува неговата траектoрија).
    454 
    455 '''Битна забелешка за реалност:'''
    456 
    457 Оваа функција користи '''линеарна интерполација по great-circle линија''' — претпоставува дека авионот лета со константна брзина по најкраткиот пат на сфера. Во вистинскиот свет:
    458 
    459  * Авионите не летаат со константна брзина (имаат фази на качување, крстарење и спуштање).
    460  * Реалните рути често отстапуваат од идеалната great-circle линија поради воздушни кориди, временски услови, забранети воздушни простори.
    461  * Реалните системи за следење на летови (Flight Tracking) користат '''посебна табела за логирање на локации''' — на пример, ADS-B транспондерите на авионите емитуваат локација секои неколку секунди, а сите тие точки се чуваат во база.
    462 
    463 Бидејќи во овој проект немаме вистински авиони, импровизираме со пресметана локација. Во документацијата експлицитно споменуваме дека ова е демонстративен пристап, не продукциски модел.
    464 
    465 '''Случаи на употреба:'''
    466 
    467  * '''Air Traffic Control визуелизација''' — приказ каде се сите авиони во моментот.
    468  * '''Информации за патници''' — „вашиот лет моментално е над Атлантскиот океан, на 70% од патот".
    469  * '''Анализа на оперативни инциденти''' — со повикување на {{{FlightAtTime(flight_id, incident_time)}}} се добива каде се наоѓал авионот кога настанал инцидентот.
    470 
    471 '''Демонстрација:'''
    472 
    473 [[Image(Screenshots/FlightAtDemo.png)]]
    474 
    475 ----
    476 
    477 == 11. Визуелизација со QGIS ==
    478 
    479 Една од најголемите предности на PostGIS е природната интеграција со '''QGIS''' — индустриски стандарден отворен GIS алат. QGIS може директно да се поврзе со нашата PostGIS база и да ги визуелизира просторните слоеви во реално време.
    480 
    481 '''Процес на конекција:'''
    482 
    483  1. Во QGIS се додава PostGIS конекција (Browser → !PostgreSQL → New Connection) со параметрите на базата.
    484  2. Сите табели и погледи со просторни колони (Airport, !FlightRoutes итн.) автоматски се препознаваат како слоеви.
    485  3. Со drag-and-drop се додаваат на мапата.
    486  4. Како подлога се користи !OpenStreetMap (преку XYZ Tiles), кој дава реален географски контекст.
    487 
    488 '''Што е визуелизирано:'''
    489 
    490  * '''Слој на аеродроми''' — секој аеродром се прикажува како точка, со label-от поставен на IATA код.
    491  * '''Слој на рути''' — секоја линија претставува лет помеѓу два аеродроми. При зголемување на zoom, се гледа конкретниот географски пат.
    492  * '''Демонстрација на функциите''' — {{{FindNearestAirport}}} и {{{FlightAtTime}}} се повикуваат преку DB Manager → SQL Window, и нивните резултати се вчитуваат како нови слоеви.
    493 
    494 QGIS проектот е зачуван како {{{SBAirlines.qgz}}} и претставува дел од испораката на оваа фаза.
    495 
    496 ----
    497 
    498 == 12. Краток преглед на новите компоненти ==
    499 
    500 || '''Компонента''' || '''Тип''' || '''Намена''' ||
    501 || {{{airport.latitude}}}, {{{airport.longitude}}} || Нови колони || Сурови географски координати ||
    502 || {{{airport.location}}} || Нова колона || PostGIS геометриска точка (geography Point, 4326) ||
    503 || {{{idx_airport_location}}} || Просторен индекс || GIST индекс за брзи просторни прашалници ||
    504 || {{{FlightRoutes}}} || Нов поглед || Сите летови како !LineString геометрии ||
    505 || {{{ScheduledFlight.distance}}} || Ажурирана колона || Реални great-circle растојанија наместо синтетички ||
    506 || {{{CalculateDistance()}}} + {{{SetDistance}}} || Нов тригер || Автоматска пресметка на дистанца при INSERT/UPDATE ||
    507 || {{{FindNearestAirport(lon, lat, km)}}} || Нова функција || Враќа аеродроми во даден радиус, сортирани по близина ||
    508 || {{{FlightAtTime(id, time)}}} || Нова функција || Пресметува локација на лет во даден момент ||
    509 
    510 ----
    511 
    512 == 13. Заклучок ==
    513 
    514 Интеграцијата на PostGIS во проектот SBAirlines не претставува површно додавање на функционалност, туку природна еволуција на моделот кон вистинскиот свет на авиокомпанијата. Со неа:
    515 
    516  * '''Аеродромите имаат реални координати''', а не само имиња и кодови.
    517  * '''Растојанијата на летовите се вистински''', што влијае врз постоечките компоненти (миљни поени, оптимизација на флота, оперативни извештаи).
    518  * '''Се отвораат нови случаи на употреба''' кои се невозможни во чист релациски модел (наоѓање најблиски аеродроми, визуализација на мрежа, следење на локација).
    519  * '''Се демонстрира просторно индексирање''' — нов вид индекс кој не беше покриен во основните фази на проектот.
    520  * '''Базата на податоци станува извор на вистина''' за визуелни алатки како QGIS, кои директно ги читаат просторните слоеви.
    521 
    522 PostGIS ја претвора нашата база од „евиденција за авиокомпанија" во „геопросторна оперативна платформа" — токму она што го користат вистинските авиокомпании во светот.