-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdatabase_schema.sql
More file actions
184 lines (165 loc) · 6.88 KB
/
Copy pathdatabase_schema.sql
File metadata and controls
184 lines (165 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
-- Urban Transit Tool - Database Schema
-- PostgreSQL + PostGIS schema for zone generation caching
-- Enable PostGIS extension
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_topology;
-- Table 1: Cities
-- Stores metadata about processed cities
CREATE TABLE IF NOT EXISTS cities (
city_id SERIAL PRIMARY KEY,
place_name VARCHAR(255) UNIQUE NOT NULL,
normalized_name VARCHAR(255) NOT NULL, -- e.g., "bandra_mumbai_india"
boundary_geom GEOMETRY(POLYGON, 4326), -- City boundary
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Indexes for cities table
CREATE INDEX IF NOT EXISTS idx_cities_place_name ON cities(place_name);
CREATE INDEX IF NOT EXISTS idx_cities_normalized ON cities(normalized_name);
CREATE INDEX IF NOT EXISTS idx_cities_boundary ON cities USING GIST(boundary_geom);
-- Table 2: Zone Generations
-- Stores metadata about each zone generation run
CREATE TABLE IF NOT EXISTS zone_generations (
generation_id SERIAL PRIMARY KEY,
city_id INTEGER REFERENCES cities(city_id) ON DELETE CASCADE,
target_population INTEGER NOT NULL,
buffer_distance FLOAT NOT NULL,
hex_resolution INTEGER,
num_zones INTEGER NOT NULL,
total_area_km2 FLOAT NOT NULL,
total_proxy_population INTEGER DEFAULT 0,
total_proxy_employment INTEGER DEFAULT 0,
processing_time_seconds FLOAT,
osm_extraction_timestamp TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_current BOOLEAN DEFAULT TRUE,
CONSTRAINT unique_generation UNIQUE(city_id, target_population, buffer_distance, hex_resolution)
);
-- Indexes for zone_generations table
CREATE INDEX IF NOT EXISTS idx_generation_city ON zone_generations(city_id);
CREATE INDEX IF NOT EXISTS idx_generation_current ON zone_generations(city_id, is_current);
CREATE INDEX IF NOT EXISTS idx_generation_created ON zone_generations(created_at DESC);
-- Table 3: Zones
-- Stores individual zone data with geometries and attributes
CREATE TABLE IF NOT EXISTS zones (
zone_pk SERIAL PRIMARY KEY,
generation_id INTEGER REFERENCES zone_generations(generation_id) ON DELETE CASCADE,
zone_id VARCHAR(50) NOT NULL,
zone_geometry GEOMETRY(POLYGON, 4326) NOT NULL,
centroid_geometry GEOMETRY(POINT, 4326) NOT NULL,
area_km2 FLOAT NOT NULL,
proxy_population INTEGER DEFAULT 0,
proxy_employment INTEGER DEFAULT 0,
dominant_landuse VARCHAR(50),
is_cbd BOOLEAN DEFAULT FALSE,
is_special_generator BOOLEAN DEFAULT FALSE,
CONSTRAINT unique_zone UNIQUE(generation_id, zone_id)
);
-- Indexes for zones table
CREATE INDEX IF NOT EXISTS idx_zones_generation ON zones(generation_id);
CREATE INDEX IF NOT EXISTS idx_zones_zone_id ON zones(zone_id);
CREATE INDEX IF NOT EXISTS idx_zones_geometry ON zones USING GIST(zone_geometry);
CREATE INDEX IF NOT EXISTS idx_zones_centroid ON zones USING GIST(centroid_geometry);
CREATE INDEX IF NOT EXISTS idx_zones_landuse ON zones(dominant_landuse);
CREATE INDEX IF NOT EXISTS idx_zones_cbd ON zones(is_cbd);
-- Table 4: Skim Matrices
-- Stores zone-to-zone skim matrices (distance, time, cost)
CREATE TABLE IF NOT EXISTS skim_matrices (
skim_id SERIAL PRIMARY KEY,
generation_id INTEGER REFERENCES zone_generations(generation_id) ON DELETE CASCADE,
origin_zone_id VARCHAR(50) NOT NULL,
destination_zone_id VARCHAR(50) NOT NULL,
distance_km FLOAT,
time_drive_min FLOAT,
time_transit_min FLOAT,
time_walk_min FLOAT,
cost_drive FLOAT,
CONSTRAINT unique_skim_pair UNIQUE(generation_id, origin_zone_id, destination_zone_id)
);
-- Indexes for skim_matrices table
CREATE INDEX IF NOT EXISTS idx_skim_generation ON skim_matrices(generation_id);
CREATE INDEX IF NOT EXISTS idx_skim_origin ON skim_matrices(origin_zone_id);
CREATE INDEX IF NOT EXISTS idx_skim_destination ON skim_matrices(destination_zone_id);
CREATE INDEX IF NOT EXISTS idx_skim_od_pair ON skim_matrices(generation_id, origin_zone_id, destination_zone_id);
-- Table 5: Connectors (Optional)
-- Stores network connectors if generated
CREATE TABLE IF NOT EXISTS connectors (
connector_id SERIAL PRIMARY KEY,
zone_pk INTEGER REFERENCES zones(zone_pk) ON DELETE CASCADE,
connector_geometry GEOMETRY(LINESTRING, 4326),
connector_type VARCHAR(50),
length_m FLOAT
);
-- Indexes for connectors table
CREATE INDEX IF NOT EXISTS idx_connectors_zone ON connectors(zone_pk);
CREATE INDEX IF NOT EXISTS idx_connectors_geometry ON connectors USING GIST(connector_geometry);
CREATE INDEX IF NOT EXISTS idx_connectors_type ON connectors(connector_type);
-- Helper function: Get latest generation for a city
CREATE OR REPLACE FUNCTION get_latest_generation(p_place_name VARCHAR)
RETURNS INTEGER AS $$
DECLARE
v_generation_id INTEGER;
BEGIN
SELECT g.generation_id INTO v_generation_id
FROM zone_generations g
JOIN cities c ON g.city_id = c.city_id
WHERE c.place_name = p_place_name
AND g.is_current = TRUE
ORDER BY g.created_at DESC
LIMIT 1;
RETURN v_generation_id;
END;
$$ LANGUAGE plpgsql;
-- Helper function: Mark generation as current (and unmark others)
CREATE OR REPLACE FUNCTION set_current_generation(p_generation_id INTEGER)
RETURNS VOID AS $$
DECLARE
v_city_id INTEGER;
BEGIN
-- Get city_id for this generation
SELECT city_id INTO v_city_id
FROM zone_generations
WHERE generation_id = p_generation_id;
-- Unmark all other generations for this city
UPDATE zone_generations
SET is_current = FALSE
WHERE city_id = v_city_id;
-- Mark this generation as current
UPDATE zone_generations
SET is_current = TRUE
WHERE generation_id = p_generation_id;
END;
$$ LANGUAGE plpgsql;
-- Create view for easy zone querying with city info
CREATE OR REPLACE VIEW zones_with_city AS
SELECT
z.zone_pk,
z.zone_id,
z.zone_geometry,
z.centroid_geometry,
z.area_km2,
z.proxy_population,
z.proxy_employment,
z.dominant_landuse,
z.is_cbd,
z.is_special_generator,
g.generation_id,
g.target_population,
g.buffer_distance,
g.created_at as generation_date,
c.place_name,
c.normalized_name
FROM zones z
JOIN zone_generations g ON z.generation_id = g.generation_id
JOIN cities c ON g.city_id = c.city_id;
-- Grant permissions (adjust as needed for your setup)
-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO urban_admin;
-- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO urban_admin;
-- Initial setup complete message
DO $$
BEGIN
RAISE NOTICE 'Urban Transit Tool database schema created successfully!';
RAISE NOTICE 'Tables: cities, zone_generations, zones, skim_matrices, connectors';
RAISE NOTICE 'View: zones_with_city';
RAISE NOTICE 'Functions: get_latest_generation, set_current_generation';
END $$;