ST_SquareGrid for a Dynamic Street Map Index

OpenStreetMap + PostGIS + 0 Intermediate Layers

The Task

Recreate the Yorkville Streets Map.

Essential Components

  • City Streets
  • Municipal boundary
  • An alphanumeric grid
  • Street index based on grid
  • Other nearby features for context

Can I Do This with OpenStreetMap Data?

Can I avoid creating any new layers?

  • Map is being made in QGIS
  • Already writing SQL query layers for the OSM data

There’s gotta be a function for this…

The Grid

Step One: Research

Thanks, DuckDuckGo!

Step Two: SQL

select (st_squaregrid(1000, ST_Transform(the_geom, 3435))).*
from kendall_areas
where tags ->> 'admin_level' = '8' and tags ->> 'name' = 'Yorkville'

Step Three: Proper Grid Size

select (st_squaregrid(10000, ST_Transform(the_geom, 3435))).*
from kendall_areas
where tags ->> 'admin_level' = '8' and tags ->> 'name' = 'Yorkville'

Step Three: Proper Grid Size

select (st_squaregrid(5000, ST_Transform(the_geom, 3435))).*
from kendall_areas
where tags ->> 'admin_level' = '8' and tags ->> 'name' = 'Yorkville'

Step Three: Proper Grid Size

select (st_squaregrid(4800, ST_Transform(the_geom, 3435))).*
from kendall_areas
where tags ->> 'admin_level' = '8' and tags ->> 'name' = 'Yorkville'

Cell Labels

Step Four: Proper Labels

with grid as (
  select (st_squaregrid(4800, ST_Transform(the_geom, 3435))).*
  from kendall_areas
  where tags ->> 'admin_level' = '8' and tags ->> 'name' = 'Yorkville'
),

indices as (
  select
  min(grid.i) as min_i,
  max(grid.j) as max_j
  from grid
)

select
  chr(i - indices.min_i + 65) as ltr,
  (j - indices.max_j - 1) * -1 as num,
  row_number() over() as id,
  geom
from grid
join indices on 1=1

The Streets

Centerlines

select
  osm_id,
  the_geom,
  tags ->> 'name' as name
from kendall_ways
where tags ->> 'highway' ~* '.*ary.*|motorway.*|trunk.*|residential.*|unclassified.*'

Keep it City-Centric

select
  osm_id,
  the_geom,
  tags ->> 'name' as name
from kendall_ways
where tags ->> 'highway' ~* '.*ary.*|motorway.*|trunk.*|residential.*|unclassified.*'
  and tags ->> 'operator' ~* '.*Yorkville.*'

Keep it City-Centric

select
  osm_id,
  the_geom,
  tags ->> 'name' as name
from kendall_ways
where tags ->> 'operator' ~* '.*Yorkville.*|.*County.*|.*Illinois.*'
  and tags ->> 'highway' is not null and tags ->> 'name' is not null

Keep it City-Centric

select
  osm_id,
  the_geom,
  tags ->> 'name' as name
from kendall_ways
where tags ->> 'operator' ~* '.*Yorkville.*|.*County.*|.*Illinois.*'
  and tags ->> 'highway' is not null and tags ->> 'name' is not null or tags ->> 'route' = 'road'

Which Cells are they In?

ST_Intersects

with idx as (
  -- our grid query from earlier
)

select 
  ST_Transform(the_geom, 3435) the_geom,
  tags ->> 'name' as name,
  ltr,
  num
from public.kendall_ways
join idx on ST_Intersects(ST_Transform(the_geom, 3435), geom)
where tags ->> 'operator' ~* '.*Yorkville.*|.*County.*|.*Illinois.*'
  and tags ->> 'highway' is not null and tags ->> 'name' is not null or tags ->> 'route' = 'road'

Rookie Mistake

Aggregate!

with idx as (
  -- our grid query from earlier
),

roads as (
  select 
    ST_Union(ST_Transform(the_geom, 3435)) the_geom,
    tags ->> 'name' as name
  from public.kendall_ways
  where tags ->> 'operator' ~* '.*Yorkville.*|.*County.*|.*Illinois.*'
    and tags ->> 'highway' is not null and tags ->> 'name' is not null or tags ->> 'route' = 'road'
  group by tags ->> 'name'
)

select
  row_number() over() id,
  name,
  string_agg(ltr || '-' || num, ', ') grid_index
  st_union(the_geom) the_geom
from roads
join idx on st_intersects(the_geom, geom)
group by name

Replacements

-- in the final select
  name label,
  case
    when name = 'West Street' then name
    when name like 'West %' then substring(name, 6) || ', West'
    when name like 'East %' then substring(name, 6) || ', East'
    when name like 'North %' then substring(name, 7) || ', North'
    when name like 'South %' then substring(name, 7) || ', South'
    else name
  end name,

Route Names

-- in the roads CTE
case
    when tags ->> 'name' is not null then tags ->> 'name'
    when tags ->> 'network' = 'US:US' then concat('US ', tags ->> 'ref')
    when tags ->> 'network' = 'US:IL' then concat('IL ', tags ->> 'ref')
  end as name

Other Stuff

Municipalities

select
  osm_id,
  the_geom,
  tags ->> 'name' name
from kendall_areas
where tags ->> 'admin_level' = '8'

Other Roads

The Map