Changes between Initial Version and Version 1 of AdvancedTopics


Ignore:
Timestamp:
06/12/26 21:33:17 (9 days ago)
Author:
231138
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedTopics

    v1 v1  
     1= Напредни бази на податоци =
     2= Бонус — PostGIS просторни прашалници =
     3== Проект: DriveNet ==
     4
     5Александар Милошевски 231138
     6Исидора Кузмановска 231052
     7Андон Михајлов 231016
     8
     9Во оваа фаза го прошируваме проектот DriveNet со PostGIS — просторно проширување за PostgreSQL кое овозможува складирање и пребарување на географски податоци. Наместо пресметување на растојанија во апликацискиот код, сите просторни операции се извршуваат директно во базата со индексирани геометриски колони.
     10
     11----
     12
     13= Зошто PostGIS? =
     14
     15Во нашата постоечка шема имаме `lat/lng` колони во следните табели:
     16
     17|| **Табела** || **Употреба** ||
     18|| `locations` || Места за качување и слегување ||
     19|| `cities` || Центри на градови ||
     20|| `location_pings` || Живо следење на возачот ||
     21|| `toll_points` || Патарински точки на рутата ||
     22|| `user_addresses` || Зачувани адреси на корисници ||
     23
     24Без PostGIS, пребарување "возења во близина" би барало пресметување на Haversine формулата во апликацискиот код за секој ред. Со PostGIS, истото се прави со еден индексиран SQL прашалник.
     25
     26----
     27
     28= Инсталација и активација =
     29
     30{{{
     31-- Активирање на PostGIS проширувањето
     32CREATE EXTENSION postgis;
     33
     34-- Верификација
     35SELECT PostGIS_Version();
     36-- Резултат: 3.6 USE_GEOS=1 USE_PROJ=1 USE_STATS=1
     37}}}
     38
     39----
     40
     41= Миграција на постоечката шема =
     42
     43==== Чекор 1 — Додавање на геометриски колони:
     44{{{
     45ALTER TABLE locations      ADD COLUMN geom GEOMETRY(Point, 4326);
     46ALTER TABLE cities         ADD COLUMN geom GEOMETRY(Point, 4326);
     47ALTER TABLE location_pings ADD COLUMN geom GEOMETRY(Point, 4326);
     48ALTER TABLE toll_points    ADD COLUMN geom GEOMETRY(Point, 4326);
     49ALTER TABLE user_addresses ADD COLUMN geom GEOMETRY(Point, 4326);
     50}}}
     51
     52==== Чекор 2 — Пополнување од постоечките lat/lng:
     53{{{
     54UPDATE locations      SET geom = ST_SetSRID(ST_MakePoint(lng, lat), 4326);
     55UPDATE cities         SET geom = ST_SetSRID(ST_MakePoint(lng, lat), 4326);
     56UPDATE location_pings SET geom = ST_SetSRID(ST_MakePoint(lng, lat), 4326);
     57UPDATE toll_points    SET geom = ST_SetSRID(ST_MakePoint(lng, lat), 4326);
     58UPDATE user_addresses SET geom = ST_SetSRID(ST_MakePoint(lng, lat), 4326);
     59}}}
     60
     61==== Чекор 3 — Просторни индекси (GIST):
     62{{{
     63CREATE INDEX idx_locations_geom      ON locations      USING GIST(geom);
     64CREATE INDEX idx_cities_geom         ON cities         USING GIST(geom);
     65CREATE INDEX idx_location_pings_geom ON location_pings USING GIST(geom);
     66CREATE INDEX idx_toll_points_geom    ON toll_points    USING GIST(geom);
     67CREATE INDEX idx_user_addresses_geom ON user_addresses USING GIST(geom);
     68}}}
     69
     70Просторните индекси користат `GIST` (Generalized Search Tree) наместо стандардниот `BTREE` — специјализирани за двородимензионални геометриски пребарувања.
     71
     72----
     73
     74= Просторни функции =
     75
     76=== 1. find_rides_near ===
     77
     78==== Опис:
     79
     80Ги наоѓа сите достапни возења чија почетна точка се наоѓа во одреден радиус од локацијата на патникот. Го заменува комплексното пресметување на растојанија во апликацискиот код со еден индексиран просторен прашалник.
     81
     82==== Сигнатура:
     83{{{
     84find_rides_near(p_lat DECIMAL, p_lng DECIMAL, p_radius_km INT DEFAULT 10)
     85RETURNS TABLE (ride_id, departure_time, driver_name, driver_avg_rating,
     86               origin_city, origin_location, destination_city,
     87               estimated_fare, seats_available, distance_km)
     88}}}
     89
     90==== Влезни параметри:
     91
     92|| **Параметар** || **Тип** || **Опис** ||
     93|| `p_lat` || DECIMAL || Географска ширина на патникот ||
     94|| `p_lng` || DECIMAL || Географска должина на патникот ||
     95|| `p_radius_km` || INT || Радиус на пребарување во км (default 10) ||
     96
     97==== Логика:
     98
     99 1. Ја зема локацијата на патникот и ја претвора во PostGIS точка со `ST_SetSRID(ST_MakePoint(p_lng, p_lat), 4326)`
     100 2. Ги филтрира сите достапни возења преку `v_available_rides` со `ST_DWithin` — ги враќа само возењата чија почетна локација е во рамки на бараниот радиус
     101 3. За секое возење го пресметува точното растојание во км со `ST_Distance` и `::geography` cast за метарска точност
     102 4. Ги сортира резултатите по растојание
     103
     104==== Имплементација:
     105{{{
     106CREATE OR REPLACE FUNCTION find_rides_near(
     107    p_lat       DECIMAL,
     108    p_lng       DECIMAL,
     109    p_radius_km INT DEFAULT 10
     110)
     111RETURNS TABLE (
     112    ride_id           INT,
     113    departure_time    TIMESTAMP,
     114    driver_name       TEXT,
     115    driver_avg_rating NUMERIC,
     116    origin_city       TEXT,
     117    origin_location   TEXT,
     118    destination_city  TEXT,
     119    estimated_fare    NUMERIC,
     120    seats_available   INT,
     121    distance_km       NUMERIC
     122)
     123LANGUAGE plpgsql
     124AS $$
     125BEGIN
     126    RETURN QUERY
     127    SELECT
     128        var.ride_id,
     129        var.departure_time,
     130        var.driver_name::TEXT,
     131        var.driver_avg_rating,
     132        var.origin_city::TEXT,
     133        var.origin_location::TEXT,
     134        var.destination_city::TEXT,
     135        var.estimated_fare,
     136        var.seats_available,
     137        ROUND(ST_Distance(
     138            l.geom::geography,
     139            ST_SetSRID(ST_MakePoint(p_lng, p_lat), 4326)::geography
     140        )::numeric / 1000, 2) AS distance_km
     141    FROM v_available_rides var
     142    JOIN locations l ON l.name = var.origin_location
     143    WHERE ST_DWithin(
     144        l.geom::geography,
     145        ST_SetSRID(ST_MakePoint(p_lng, p_lat), 4326)::geography,
     146        p_radius_km * 1000
     147    )
     148    ORDER BY distance_km;
     149END;
     150$$;
     151}}}
     152
     153==== Пример за употреба:
     154{{{
     155-- Пронајди возења во близина на центарот на Скопје (радиус 10km)
     156SELECT * FROM find_rides_near(41.9981, 21.4254, 10);
     157
     158-- Пронајди возења во близина на одредена локација (радиус 25km)
     159SELECT * FROM find_rides_near(41.9981, 21.4254, 25);
     160}}}
     161
     162==== Споредба — без PostGIS vs со PostGIS:
     163
     164Без PostGIS (Haversine во SQL):
     165{{{
     166SELECT r.id, ...
     167FROM rides r
     168JOIN locations l ON ...
     169WHERE (
     170    6371 * acos(
     171        cos(radians(41.9981)) * cos(radians(l.lat)) *
     172        cos(radians(l.lng) - radians(21.4254)) +
     173        sin(radians(41.9981)) * sin(radians(l.lat))
     174    )
     175) <= 10;
     176-- Нема индекс — целосен скен на табелата
     177}}}
     178
     179Со PostGIS:
     180{{{
     181WHERE ST_DWithin(
     182    l.geom::geography,
     183    ST_SetSRID(ST_MakePoint(21.4254, 41.9981), 4326)::geography,
     184    10000
     185);
     186-- Користи GIST индекс — директен просторен пристап
     187}}}
     188
     189----
     190
     191=== 2. find_nearest_stop ===
     192
     193==== Опис:
     194
     195За дадено возење и локација на патник, ги враќа сите постојки на рутата сортирани по растојание. Му помага на патникот да ја избере најблиската постојка за качување.
     196
     197==== Сигнатура:
     198{{{
     199find_nearest_stop(p_ride_id INT, p_lat DECIMAL, p_lng DECIMAL)
     200RETURNS TABLE (stop_id, stop_order, location_name, distance_meters)
     201}}}
     202
     203==== Влезни параметри:
     204
     205|| **Параметар** || **Тип** || **Опис** ||
     206|| `p_ride_id` || INT || ID на возењето ||
     207|| `p_lat` || DECIMAL || Географска ширина на патникот ||
     208|| `p_lng` || DECIMAL || Географска должина на патникот ||
     209
     210==== Логика:
     211
     212 1. Го наоѓа `route_id` на возењето
     213 2. За сите постојки на рутата го пресметува растојанието во метри од локацијата на патникот со `ST_Distance`
     214 3. Ги сортира по растојание — најблиската постојка е прва
     215
     216==== Имплементација:
     217{{{
     218CREATE OR REPLACE FUNCTION find_nearest_stop(
     219    p_ride_id INT,
     220    p_lat     DECIMAL,
     221    p_lng     DECIMAL
     222)
     223RETURNS TABLE (
     224    stop_id         INT,
     225    stop_order      INT,
     226    location_name   TEXT,
     227    distance_meters NUMERIC
     228)
     229LANGUAGE plpgsql
     230AS $$
     231BEGIN
     232    RETURN QUERY
     233    SELECT
     234        rs.id          AS stop_id,
     235        rs.stop_order,
     236        l.name::TEXT   AS location_name,
     237        ROUND(ST_Distance(
     238            l.geom::geography,
     239            ST_SetSRID(ST_MakePoint(p_lng, p_lat), 4326)::geography
     240        )::numeric, 2) AS distance_meters
     241    FROM route_stops rs
     242    JOIN locations l ON l.id = rs.location_id
     243    WHERE rs.route_id = (SELECT route_id FROM rides WHERE id = p_ride_id)
     244    ORDER BY distance_meters;
     245END;
     246$$;
     247}}}
     248
     249==== Пример за употреба:
     250{{{
     251-- Најди најблиска постојка за возење 1, патник е во центарот на Скопје
     252SELECT * FROM find_nearest_stop(1, 41.9981, 21.4254);
     253}}}
     254
     255----
     256
     257= Дополнителни просторни прашалници =
     258
     259==== Локации во рамки на одреден радиус:
     260{{{
     261SELECT
     262    l.name,
     263    c.name AS city,
     264    ROUND(ST_Distance(
     265        l.geom::geography,
     266        ST_SetSRID(ST_MakePoint(21.4254, 41.9981), 4326)::geography
     267    )::numeric / 1000, 2) AS distance_km
     268FROM locations l
     269JOIN cities c ON c.id = l.city_id
     270ORDER BY distance_km
     271LIMIT 10;
     272}}}
     273
     274==== Патарински точки долж рута:
     275{{{
     276SELECT tp.name, tp.price_car
     277FROM toll_points tp
     278WHERE ST_DWithin(
     279    tp.geom::geography,
     280    ST_MakeLine(
     281        ST_SetSRID(ST_MakePoint(21.43, 41.99), 4326),
     282        ST_SetSRID(ST_MakePoint(22.64, 41.04), 4326)
     283    )::geography,
     284    500
     285);
     286}}}
     287
     288==== Најблизок град до одредена точка:
     289{{{
     290SELECT
     291    name,
     292    ROUND(ST_Distance(
     293        geom::geography,
     294        ST_SetSRID(ST_MakePoint(21.4254, 41.9981), 4326)::geography
     295    )::numeric / 1000, 2) AS distance_km
     296FROM cities
     297ORDER BY distance_km
     298LIMIT 5;
     299}}}
     300
     301----
     302
     303= Резиме =
     304
     305|| **Функција** || **PostGIS функција** || **Опис** ||
     306|| Растојание меѓу две точки || `ST_Distance` || Точно растојание во метри ||
     307|| Точки во радиус || `ST_DWithin` || Индексирано просторно филтрирање ||
     308|| Креирање точка || `ST_MakePoint` || Од lng/lat во геометрија ||
     309|| Поставување координатен систем || `ST_SetSRID(..., 4326)` || WGS84 — стандард за GPS ||
     310|| Просторен индекс || `USING GIST` || Наместо BTREE за 2D пребарување ||
     311|| Geography cast || `::geography` || Метарска точност наместо степени ||
     312
     313Клучната предност на PostGIS е комбинацијата од `ST_DWithin` + `GIST` индекс — просторното филтрирање се извршува директно во индексот без скенирање на целата табела, слично како `idx_rides_status_dep` го забрза пребарувањето на возења.