MODUL 4 - LEKCIJA 3

Prostorni Podaci (Spatial Data)

Rad sa geografskim koordinatama, mapama i prostornim proračunima

⏱️ Trajanje: ~2.5 časa 📚 Nivo: Srednji 🎯 Praktični primjeri: 4

📖 Planeta u bazi podataka

SQL Server nije samo za brojeve i tekst. On podržava Spatial Data (prostorne podatke) koji se koriste u GPS aplikacijama, sistemima za dostavu (pronalaženje najbližeg kurira) ili u arhitekturi. Svi ovi podaci se zasnivaju na objektno-orijentisanim tipovima koji dolaze iz OGC (Open Geospatial Consortium) standarda.

🧩 Geography vs Geometry

1. GEOGRAPHY (Okrugla zemlja)

Koristi Latituda/Longituda koordinate. Uzima u obzir zakrivljenost zemlje (ellipsoidal model). Kada računate udaljenost Sarajevo-London, Geography će dati tačan rezultat "preko horizonta".

2. GEOMETRY (Ravna površina)

Koristi X/Y koordinate na ravni (planar model). Idealno za tlocrte zgrada, mape igara ili bilo šta što staje na ravan list papira.

📏 Well-Known Text (WKT)

Podatke u bazu unosimo pomoću standardizovanog tekstualnog formata:

🛠️ Kreiranje i mjerenje udaljenosti
DECLARE @Sarajevo GEOGRAPHY = geography::STGeomFromText('POINT(18.413 43.856)', 4326);
DECLARE @Mostar GEOGRAPHY = geography::STGeomFromText('POINT(17.807 43.343)', 4326);

-- Udaljenost u metrima
SELECT @Sarajevo.STDistance(@Mostar) / 1000.0 AS Udaljenost_km;

Napomena: 4326 je SRID (Spatial Reference ID) za WGS 84 standard koji koristi GPS.

🔍 Ključne Metode za Prostorne Podatke

SQL Server pruža bogat set metoda za rad sa prostornim podacima. Evo najvažnijih:

Metode za Mjerenje i Računanje

Metode za Provjeru Relacija

Metode za Konverziju i Konstrukciju

🛠️ Primjeri Korištenja Metoda
-- 1. STBuffer - Kreiranje zone oko tačke
DECLARE @Restaurant GEOGRAPHY = geography::STGeomFromText('POINT(18.413 43.856)', 4326);
DECLARE @DeliveryZone GEOGRAPHY = @Restaurant.STBuffer(2000); -- 2km zona
-- @DeliveryZone je sada poligon koji predstavlja krug od 2km oko restorana

-- 2. STIntersects - Provjera da li se objekti preklapaju
DECLARE @UserLocation GEOGRAPHY = geography::STGeomFromText('POINT(18.415 43.857)', 4326);
SELECT 
    CASE 
        WHEN @UserLocation.STIntersects(@DeliveryZone) = 1 
        THEN 'U zoni dostave' 
        ELSE 'Izvan zone dostave' 
    END AS StatusDostave;

-- 3. STArea - Računanje površine
DECLARE @CityBoundary GEOGRAPHY = geography::STGeomFromText(
    'POLYGON((18.0 43.7, 18.5 43.7, 18.5 44.0, 18.0 44.0, 18.0 43.7))', 4326
);
SELECT @CityBoundary.STArea() / 1000000 AS PovrsinaKm2; -- Površina u km²

-- 4. STContains - Provjera da li grad sadrži tačku
SELECT 
    CASE 
        WHEN @CityBoundary.STContains(@Restaurant) = 1 
        THEN 'Restoran je u gradu' 
        ELSE 'Restoran je izvan grada' 
    END AS Lokacija;

-- 5. Kombinovanje metoda - Pronalaženje tačaka u zoni
SELECT 
    Name,
    Location.STDistance(@Restaurant) AS UdaljenostMetri,
    Location.STIntersects(@DeliveryZone) AS UZoni
FROM Restaurants
WHERE Location.STIntersects(@DeliveryZone) = 1
ORDER BY Location.STDistance(@Restaurant);
GO

🚀 Spatial Indeksi: Ubrzanje Prostornih Upita

Prostorni upiti mogu biti ekstremno spori na velikim tabelama jer zahtijevaju kompleksne geometrijske izračune. Spatial Index koristi grid sistem (tesselation) da ubrza pretrage.

Kako Spatial Index Radi?

Spatial Index dijeli prostor u grid (mrežu) kvadrata različitih nivoa:

🛠️ Kreiranje Spatial Indeksa
-- Kreiranje Spatial Index-a na Geography koloni
CREATE SPATIAL INDEX IX_Restaurants_Location
ON Restaurants(Location)
USING GEOGRAPHY_GRID
WITH (
    GRIDS = (LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM),
    CELLS_PER_OBJECT = 16
);
GO

-- GRIDS opcije:
-- LOW, MEDIUM, HIGH - gustoća grid-a (HIGH = više kvadrata, precizniji ali veći indeks)
-- CELLS_PER_OBJECT - koliko grid ćelija može jedan objekat zauzeti (default 16)

-- Provjera Spatial Index-a
SELECT 
    name AS IndexName,
    type_desc AS IndexType,
    is_spatial AS IsSpatial
FROM sys.indexes
WHERE object_id = OBJECT_ID('Restaurants')
  AND is_spatial = 1;
GO

🔑 Best Practices za Spatial Indekse

  • Koristite za velike tabele: Spatial indeksi su najkorisniji na tabelama sa 1000+ redova
  • GEOGRAPHY_GRID vs GEOMETRY_GRID: Koristite odgovarajući tip ovisno o tipu kolone
  • CELLS_PER_OBJECT: Povećajte ako imate velike objekte (npr. države, provincije)
  • Grid nivoi: Viši nivoi = precizniji ali veći indeks

🎯 Praktične Vježbe

Vježba 1: Filter za Dostavu Hrane

Imate tabelu Restaurants sa geografskom kolonom Location. Korisnik se nalazi na koordinati @UserLoc. Pronađite sve restorane u krugu od 2km.

💡 Rješenje sa Spatial Index-om
-- Kreiranje tabele sa prostornim podacima
CREATE TABLE Restaurants (
    RestaurantID INT PRIMARY KEY IDENTITY(1,1),
    Name NVARCHAR(100) NOT NULL,
    Location GEOGRAPHY NOT NULL,
    CuisineType NVARCHAR(50),
    Rating DECIMAL(2,1)
);
GO

-- Kreiranje Spatial Index-a (KRITIČNO za performanse!)
CREATE SPATIAL INDEX IX_Restaurants_Location
ON Restaurants(Location)
USING GEOGRAPHY_GRID
WITH (
    GRIDS = (LEVEL_1 = MEDIUM, LEVEL_2 = MEDIUM, LEVEL_3 = MEDIUM, LEVEL_4 = MEDIUM),
    CELLS_PER_OBJECT = 16
);
GO

-- Umetanje test podataka
INSERT INTO Restaurants (Name, Location, CuisineType, Rating)
VALUES 
    ('Restoran A', geography::STGeomFromText('POINT(18.413 43.856)', 4326), 'Bosanska', 4.5),
    ('Restoran B', geography::STGeomFromText('POINT(18.420 43.860)', 4326), 'Italijanska', 4.8),
    ('Restoran C', geography::STGeomFromText('POINT(18.400 43.850)', 4326), 'Azijska', 4.2);
GO

-- Pronalaženje restorana u krugu od 2km
DECLARE @UserLoc GEOGRAPHY = geography::STGeomFromText('POINT(18.413 43.856)', 4326);
DECLARE @SearchRadius GEOGRAPHY = @UserLoc.STBuffer(2000); -- 2km = 2000 metara

SELECT 
    Name,
    CuisineType,
    Rating,
    Location.STDistance(@UserLoc) AS UdaljenostMetri,
    Location.STDistance(@UserLoc) / 1000.0 AS UdaljenostKm
FROM Restaurants
WHERE Location.STIntersects(@SearchRadius) = 1
ORDER BY Location.STDistance(@UserLoc);
GO

Vježba 2: Pronalaženje Najbližeg Restorana

Pronađite najbliži restoran korisniku bez kreiranja buffer zone.

💡 Rješenje sa TOP 1
-- Pronalaženje najbližeg restorana
DECLARE @UserLoc GEOGRAPHY = geography::STGeomFromText('POINT(18.413 43.856)', 4326);

SELECT TOP 1
    Name,
    CuisineType,
    Rating,
    Location.STDistance(@UserLoc) / 1000.0 AS UdaljenostKm
FROM Restaurants
ORDER BY Location.STDistance(@UserLoc);
GO

Vježba 3: Provjera da li je Tačka u Poligonu

Kreirajte poligon koji predstavlja granice grada i provjerite da li su restorani u gradu.

💡 Rješenje
-- Kreiranje poligona grada (Sarajevo približno)
DECLARE @CityBoundary GEOGRAPHY = geography::STGeomFromText(
    'POLYGON((
        18.3 43.8,   -- Jugozapad
        18.5 43.8,   -- Jugoistok
        18.5 43.9,   -- Sjeveroistok
        18.3 43.9,   -- Sjeverozapad
        18.3 43.8    -- Zatvaranje poligona (mora biti ista kao prva tačka)
    ))', 4326
);

-- Provjera da li su restorani u gradu
SELECT 
    Name,
    Location.STWithin(@CityBoundary) AS UGradu,
    Location.STDistance(@CityBoundary) AS UdaljenostOdGraniceMetri
FROM Restaurants
WHERE Location.STWithin(@CityBoundary) = 1;
GO

✅ Zaključak

Spatial podaci otvaraju vrata za "Location-aware" aplikacije:

📚 Sljedeća Lekcija

U Lekciji 4.4 zaključujemo ovaj modul pričom o BLOB-ovima. Naučićete kako pravilno čuvati slike i dokumente koristeći FILESTREAM.