/posts  ·   /talks  ·   /boat  ·   /#30DayMapChallenge 2020 | 2022  ·  /About

2022 / Day 30: Remix

I was really planning to rework 2022 / Day 19: Globe to x-axis meters in logarithmic scale but it looked really boring. So instead today I’m going to do a 1-degree graticule (re: 2022 / Day 13: A five minute map) for a flat earth map (re: 2022 / Day 20: Favourite).

The SQL itself is much simpler because it essentially only needs to draw a set of concentric rings around the Null Island spanning out 180 degrees with a 1 degree step (as parallels). This can be done straight on the location, not needing to st_translate everything from the pole (because there’s no need to measure any real distances). Meaning an incremental st_buffer coupled with st_boundary will suffice. This is done in the CTE paral.

For the meridian lines from the Null Island the CTE called merid constructs a line from the origin to every point measured 1 degree apart along a circle that covers the whole World - essentially a doing a st_lineinterpolatepoint every 1/360 fraction along the st_boundary of a st_buffer set at 180 degrees from the point of origin st_point(0,0,4326)::geometry. It’s important to do this using the geometry type, not geography because otherwise the Earth would be assumed to be round. Also, as the first node a buffer will be at 90 degrees clockwise from north, the query includes a st_rotate for radians(90) so the first point will be at st_azimuth 0.

The transformation from epsg:4326 to epsg:3857 is done using QGIS. Because PostGIS will complain with a transform: tolerance condition error (-20). QGIS will survive with linestrings going out of the epsg:3857 bounds ( too far North/South) it seems as long as the first and last point of the linestring are within the bounds. That’s the reason why the merid lines are chopped to the correct length with doing a st_intersection with the World’s bounds from the CTE bounds.

output image

with
    bounds as (
        select
            st_envelope(
                st_collect(
                    st_point(-180,89,4326),
                    st_point(180,-89,4326)
                )
            ) as geom
    ),
    paral as (
        select
            s, s::varchar as lbl,
            case
                when mod(s,5)=0 then 1
                else 0
            end as deg,
            case
                when mod(s,10)=0 then 1
                else 0
            end as as_lbl,
	          st_boundary(
                st_buffer(
                    st_point(0, 0, 4326),
                    s,
                    'quad_segs=80'
                )
            ) geom
        from
            generate_series(0, 180, 1)s
    ),
    merid as (
        select
            s,
            case
                /* don't show labels for these*/
                when s = 0 then null
                when s = 90 then null
                /* otherwise go with pi fractions*/
                when s = 10 then 'π/18'
                when s = 20 then 'π/9'
                when s = 30 then 'π/6'
                when s = 40 then '2π/9'
                when s = 50 then '5π/18'
                when s = 60 then 'π/3'
                when s = 70 then '7π/18'
                when s = 80 then '4π/9'
                when s = 90 then 'π/2'
                when s = 100 then '5π/9'
                when s = 110 then '11π/18'
                when s = 120 then '2π/3'
                when s = 130 then '13π/18'
                when s = 140 then '7π/9'
                when s = 150 then '5π/6'
                when s = 160 then '8π/9'
                when s = 170 then '17π/18'
                when s = 180 then 'π'
                when s = 190 then '-17π/18'
                when s = 200 then '-8π/9'
                when s = 210 then '-5π/6'
                when s = 220 then '-7π/9'
                when s = 230 then '-13π/18'
                when s = 240 then '-2π/3'
                when s = 250 then '-11π/18'
                when s = 260 then '-5π/9'
                when s = 270 then '-π/2'
                when s = 280 then '-4π/9'
                when s = 290 then '-7π/18'
                when s = 300 then '-π/3'
                when s = 310 then '-5π/18'
                when s = 320 then '-2π/9)'
                when s = 330 then '-π/6)'
                when s = 340 then '-π/9'
                when s = 350 then '-π/18'
                when s = 360 then '-π'
            end as lbl,
            case
                when mod(s,5)=0 then 1
                else 0
            end as deg,
            case
                when mod(s,10)=0 then 1
                else 0
            end as as_lbl,
            st_intersection(b.geom, d.geom) as geom
        from (
            select
                s,
                st_makeline(
                    st_point(0,0,4326),
                    st_lineinterpolatepoint(
                        st_boundary(
                            st_rotate(
                                st_buffer(
                                    st_point(0,0,4326),
                                    180
                                ),
                                radians(90)
                            )
                        ),
                        s/360.0
                    )
                ) as geom
            from
                generate_series(0,360,1)s
        ) d, bounds b
    )
select     
    row_number() over()::int as oid,
    s, lbl, deg, as_lbl, cl, geom
from (
    select s, lbl, deg, as_lbl, geom, 'merid' as cl from merid
    union all
    select s, lbl, deg, as_lbl, geom, 'paral' as cl from paral
) d
;